Exceptions

Throwing

throw takes an exception and throws it.
error is a generic kind of exception used for program logic errors.

main void() throw error "boo"

Since a throw expression won't actually return normally, it can be any type.

main void() s string = throw error "boo" log s

Custom exception types

exception is a variant type, so you can add new case types. exception has a single method show.
(See Interfaces and variants.)

main void() throw my-exception my-exception record exception case show string(_ my-exception) "custom error message"

Assert and forbid

assert throws an error if its argument is false. forbid throws an error if its argument is true.

main void() assert 1 + 1 == 2 forbid 2 * 2 == 4 # throws

An optional else clause specifies the exception to throw.

main void() assert 1 + 1 == 3 else error "Not enough?"

assert and forbid work with option conditions.
The option from an assert will be available as a non-option after it.
The option from a forbid will be available as a non-option in its else clause.

main void() assert x = half 4 log x # Will throw forbid y = half 4 else error "Did not expect {y}" half nat?(a nat) if is-even a then a / 2

is is also a kind of assertion. Prefer this over assert x == y.

main void() 1 is 2 # fails

Finally

A finally expression has right and below subexpressions.
If first runs the below expression.
It then runs the right expression, whether or not the below expression threw an exception.

Below, the right expression is log "right" and the below expression is the sequence log "below" and log 1 / 0 .

main void() finally log "right" log "below" log 1 / 0

The value of a finally expression is the value of the below expression. The right expression must be void.

main void() result = finally log "end" "value" log "result", result

The right expression can be an indented block.
Below, the right expression is on lines 3 and 4 and the below expression is just log 1 / 0 .

main void() finally log "a" log "b" log 1 / 0

Catching exceptions

A try expression first evaluates the try block. If that threw the caught exception type, it evaluates the catch block instead.

main void() log try 1 / 0 catch as divide-by-0 7

The catch has no effect if some other kind of exception is thrown.

main void() log try 1 - 2 catch as divide-by-0 7

A local name between catch and as will have the exception value.

main void() log try to::nat8 256 catch e as unary-out-of-range log "{nat64! e.input} won't fit in a nat8" 7

There can be multiple catch blocks for different exception types.

main void() log try 1 / 0 catch as unary-out-of-range 7 catch as divide-by-0 8

If there is no as clause, it catches every exception.

main void() log try 1 / 0 catch e log "Something happened: {e}" 7

Common exceptions

Throw not-implemented if something is intentionally not implemented.
Throw todo if something hasn't been implemented, but should be.
Throw unreachable if it should not be possible to get to the throw expression.

main void() log describe 1 log describe 2 describe string(a nat) match a % 2 as 0 "even" as 1 "odd" else throw unreachable

Testing exceptions

Use this pattern to test functions that should throw exceptions:

test try _ = to::string 1::json should-throw catch e as deserialize-error e is value: 1