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: ( )