Chapter 6
Errors and functions
Things go wrong: a key is missing, a list is empty, a number is divided by zero. This chapter is about what happens then, and about a second way to define words, one whose meaning cannot change underneath it.
Exceptions
When a word cannot do its job it throws an exception. An exception
is a table with four entries: type, a short name for the kind of
problem; message, a sentence for people; trace, the
words it passed through; and data, details. If nothing handles it,
the program stops and shows the exception and the values stack under it:
# 0 # 10 /
Unhandled exception
( { "data":{ } "message":"cannot divide by 0" "trace":( "/" ) "type":"InvalidArguments" } 10 0 )
A word that fails leaves its operands where they were, so the values stack shows exactly what it was given.
Handling them
trust takes two lists: a body and a handler. It runs the body.
If the body throws, the rest of the body is skipped and the handler runs, with
the exception on top of the values stack.
{ # 0 # 10 / } { \ message swap get debug } trust
" carried on " debug
cannot divide by 0 carried on values: ( 10 0 )
If the body does not throw, the handler never runs:
{ " fine " debug } { " not reached " debug } trust
fine values: ( )
What the handler finds
The handler starts with the exception on top of the values stack. Under it
is whatever was on the stack when the exception was thrown; trust
does not clean up. Values the body pushed before the throw are still there:
{ # 1 # 2 " boom " exception throw } { \ message swap get debug } trust
boom values: ( 2 1 )
A built-in word that fails leaves its operands, so a handler for
# 0 # 10 / finds the exception, then 0 and 10. A handler that
should leave the stack as it was before the body ran must del
the leftovers itself. (Functions, at the end of this chapter, behave
differently.)
Throwing your own
exception turns a message into an exception and
throw throws it. Its type is
InvalidArguments and its trace starts with the word
exception.
{ dup # 0 swap less-than { " negative " exception throw } if } => check
{ # 5 check debug # -2 check debug } { \ message swap get debug } trust
5 negative values: ( -2 )
Words that keep their meaning
A word made with => looks up every name when it runs. That is
what lets it call itself, and it also means that changing a name changes the
word:
# 1 = x
{ x } => show
# 2 = x
show debug
2 values: ( )
=: defines a word the same way, but the word runs in the macro
table as it was when the word was defined. Names it uses keep the
meaning they had then:
# 1 = x
{ x } =: show
# 2 = x
show debug
1 values: ( )
Such a word is called a function. Because it cannot see names defined after it, a function cannot call itself. In return, it adds its own name to the trace of any exception that passes through it, and it puts the values stack back the way it was found before letting the exception go on:
{ tuple pop } =: bad
{ bad } { \ trace swap get debug } trust
( "bad" "pop" ) values: ( )
Exercise 6.1
Divide 10 by 0 inside a trust. If that fails, print
infinity instead, and leave nothing on the values stack.
A correct answer prints:
infinity values: ( )
One answer
{ # 0 # 10 / debug } { del del del " infinity " debug } trust
infinity values: ( )
Exercise 6.2
Define a word price that takes a fruit name and gives its price
from a table with apples at 3 and pears at 5, or 0 for any fruit not in the
table. Print the price of pears and then of figs.
A correct answer prints:
5 0 values: ( )
One answer
[ # 3 \ apples pair # 5 \ pears pair ] table = prices
{ dup prices has { prices get } { del # 0 } either } => price
\ pears price debug
\ figs price debug
5 0 values: ( )
Exercise 6.3
Define a function half with =: that divides an even
number by 2 and throws an exception with the message odd for an
odd one. Print half of 10, then try half of 7 and print the message.
A correct answer prints:
5 odd values: ( )
One answer
{ dup # 2 swap % # 0 equals
{ # 2 swap / }
{ " odd " exception throw }
either } =: half
# 10 half debug
{ # 7 half debug } { \ message swap get debug del } trust
5 odd values: ( )