Options

Option types

An option can be empty or contain a single value.
An option type is written as foo?.

() returns an empty option.

Any value can implicitly convert to a non-empty option containing it. So any nat can implicitly convert to a nat?.

main void() empty nat? = () log empty non-empty nat? = 1 log non-empty

Options are useful for functions that might not have a result:

main void() log half 9 log half 10 half nat?(a nat) if is-even a a / 2

"if" and options

To get the value inside an option, test the option in the condition of an if.
Inside the if body, the option will implicitly convert to a non-option.

The below example uses first from the standard library, which returns the first element in an array if one exists.

main void() log describe () log describe (1, 2, 3) describe string(a nat[]) x = first a if x "Array starting with {x}" else "Empty array"

The local can be introduced inside the if.

main void() log describe () log describe (1, 2, 3) describe string(a nat[]) if x = first a "Array starting with {x}" else "Empty array"

You can even introduce a local in the left side of an and expression.

main void() log starts-the-same (), () log starts-the-same (1, 2), () log starts-the-same (1, 1, 1), (1, 2, 3) starts-the-same bool(a nat[], b nat[]) (x = first a) and (y = first b) and x == y

Option operations

a! forces an option. This gets the inner value or throws an exception if the option is empty.
If the ! comes after a function name as in half! 4, it forces function's return value.

main void() log (half 4)! # Equivalent to above: log half! 4 half nat?(a nat) if is-even a then a / 2

a or b is analogous to the boolean or.
It first evaluates a. If that is a non-empty option, it is returned. Otherwise, it evaluates b.
In other words, a or b or... evaluates options and returns the first non-empty one.

main void() a nat? = () b nat? = 1 c nat? = 2 log (a or a) log (a or b) log (b or c) log (a or a or a or a or c)

a else b is like a or b, but expects b to be non-optional.
While a or b takes two options of the same type, a else b only takes an option on the left.
So the else expression is non-optional, since it always has b to fall back on.

main void() a nat? = () b nat? = 1 log (a else 1) log (b else 2)

Optional calls

?foo calls foo if all of its arguments are non-empty options.
Otherwise it skips the call and returns an empty option.

main void() log ?foo 1.half, 4.half log ?foo 2.half, 3.half log ?foo 2.half, 4.half foo nat(a nat, b nat) a + b half nat?(a nat) if is-even a then a / 2

?foo can also return void if that is the expected type.

main void() ?log 9.half # does nothing ?log 10.half half nat?(a nat) if is-even a then a / 2

This also works with dot-calls: x.?foo

main void() 9.half.?log # does nothing 10.half.?log half nat?(a nat) if is-even a then a / 2

It works for subscript calls too:

main void() a nat[]? = () log a?[0] b nat[]? = 1, 2, 3 log b?[0]

Useful option functions

from-option converts an option to any collection.

main void() log from-option::nat[] half 1 log from-option::nat[] half 2 # Works well with '--' log :: nat[] -- from-option half 3 # adds nothing -- from-option half 4 # adds '2' half nat?(a nat) if is-even a then a / 2