ATS is a very rich programming language.
The datatype for optional values can be defined as follows:
datatype option (a:t@ype+) = Some of a | None of ()
Say we want to implement a function that returns the head of a given list:
fun{a:t0p}
list0_get_head (xs: list0 (a)): Option (a) =
case+ xs of
| list0_cons (x, _) => Some{a} (x) | list0_nil () => None ()
There is really no surprise here. The problem with optional values is that
they are heap-allocated and GC is needed to reclaim the memory they occupy.
If you do embedded programming, then you probably do not want to use them.
The following type is for linear optional values:
datavtype option_vt (a:vt@ype+) = Some_vt of a | None_vt of ()
Now the list0_get_head function can be implemented as follows:
fun{a:t0p}
list0_get_head (xs: list0 (a)): Option_vt (a) =
case+ xs of
| list0_cons (x, _) => Some_vt{a} (x) | list0_nil () => None_vt ()
Linear optional values are heap-allocated but they can be safely freed
manually
(with no need for GC).
There is another version of optional values that are stack-allocated:
fun{a:t0p}
list0_get_head (xs: list0 (a), res: &a? >> opt(a, b)): #[b:bool] bool (b) =
case+ xs of
| list0_cons (x, _) => let
val () = res := x
prval () = opt_some{a}(res)
in
true
end
| list0_nil () => let
prval () = opt_none{a}(res)
in
false
end
Essentially, we have
opt (a, true) = a and opt (a, false) = a?
This is what I call safe C-style programming. The return value indicates
whether a valid value is stored in the call-by-reference argument [res].
There
is no safe way to use the value stored in [res] without first checking that
the
returned boolean value is true.
There are plenty functions in libats that use ‘opt’.