7.1.52. Option<T> and Result<T, E>
daslib/option and daslib/result are two small template-structure modules
that give daslang a principled way to express “value or nothing” and “value or
error” for ordinary value types. They compose through the existing |> pipe.
Option<T> models absence. Result<T, E> models failure with a
reason. Prefer them over sentinel return values (-1, "") or nullable
pointers (T?).
The payload may be any type, including non-copyable ones (array<T>,
table<K;V>, lambdas) — see Non-copyable payloads.
Prerequisites: familiarity with template structures, blocks, and the pipe
operator |>.
options gen2
require daslib/option
require daslib/result
7.1.52.1. Option<T>
7.1.52.1.1. Construction
some(v) wraps a value; the payload type is inferred from v.
none(type<T>) declares an empty option — the type witness is required
because there is no value to infer from.
let a = some(42) // Option<int>
let b = none(type<int>) // Option<int>, empty
let c = some("hello") // Option<string>
7.1.52.1.2. Queries
is_some(o) and is_none(o) return bool. Pipe them through |> for
readability.
if (a |> is_some) {
// ...
}
7.1.52.1.3. Transforming values — map and and_then
map applies a block to the payload and re-wraps the result. none
propagates unchanged.
let doubled = some(21) |> map() $(x : int) { return x * 2; }
// doubled |> unwrap == 42
and_then is the monadic bind — the block itself returns an Option. Use
it to chain fallible steps; the chain short-circuits on the first none.
def safe_parse_positive(s : string) : Option<int> {
if (s == "1") { return some(1); }
return none(type<int>)
}
let r = some("1") |> and_then() $(s : string) { return safe_parse_positive(s); }
// r |> unwrap_or(-1) == 1
7.1.52.1.4. Filtering and fallbacks
let kept = some(10) |> filter() $(x : int) { return x > 5; } // some(10)
let gone = some(3) |> filter() $(x : int) { return x > 5; } // none
let eager = none(type<int>) |> or_value(99) // some(99)
let lazy = none(type<int>) |> or_else() $ { return some(expensive()); }
7.1.52.1.5. Extraction
some(5) |> unwrap // 5 — panics on none
none(type<int>) |> unwrap_or(7) // 7
some(5) |> unwrap_or_else() $ { return compute(); } // 5 (block not called)
none(type<string>) |> unwrap_or_default // ""
7.1.52.1.6. Operators
?? reads like “unwrap or this default”:
some(5) ?? 0 // 5
none(type<int>) ?? 42 // 42
== is structural — same tag, and when some, equal payloads:
some(5) == some(5) // true
some(5) == some(6) // false
none(type<int>) == none(type<int>) // true
7.1.52.1.7. Side-effect combinators
some(3) |> if_some() $(x : int) { do_work(x); }
none(type<int>) |> if_none() $ { report_missing(); }
7.1.52.1.8. Pairing two options with zip
let paired = zip(some(1), some(2.5f)) // some(tuple(1, 2.5f))
let nope = zip(some(1), none(type<float>)) // none
7.1.52.2. Result<T, E>
7.1.52.2.1. Construction
ok(v, type<E>) and err(e, type<T>) build the two sides. The second
argument is a type witness for the “other” side:
let r = ok(42, type<string>) // Result<int, string>, ok
let e = err("boom", type<int>) // Result<int, string>, err
7.1.52.2.2. Queries
r |> is_ok // true
e |> is_err // true
7.1.52.2.3. Transforming
map rewrites the ok payload; map_err rewrites the err payload. The
other side passes through unchanged.
let doubled = ok(3, type<string>) |> map() $(x : int) { return x * 2; }
// doubled |> unwrap == 6
let wrapped = err("boom", type<int>) |> map_err() $(s : string) { return "<{s}>"; }
// wrapped |> unwrap_err == "<boom>"
7.1.52.2.4. Monadic chaining — and_then and or_else
and_then chains ok values through more fallible computation; or_else
recovers from errors.
def parse_digit(s : string) : Result<int; string> {
if (s == "1") { return ok(1, type<string>); }
return err("not a digit: {s}", type<int>)
}
let good = ok("1", type<string>) |> and_then() $(s : string) { return parse_digit(s); }
// good |> unwrap_or(-1) == 1
let recovered = err("boom", type<int>) |> or_else() $(e : string) {
return ok(99, type<string>)
}
// recovered |> unwrap == 99
7.1.52.2.5. Extraction
Same shape as Option, plus unwrap_err for the error side:
ok(42, type<string>) |> unwrap // 42
err("e", type<int>) |> unwrap_or(17) // 17
err("xx", type<int>) |> unwrap_or_else() $(e : string) { return length(e); } // 2
err("boom", type<int>) |> unwrap_err // "boom"
7.1.52.2.6. Bridging to Option
to_option discards the error; err_to_option discards the value:
ok(42, type<string>) |> to_option // some(42)
err("x", type<int>) |> to_option // none
err("x", type<int>) |> err_to_option // some("x")
7.1.52.3. Non-copyable payloads
Option<T> and Result<T, E> work for any payload type, including
non-copyable ones such as array<T>, table<K;V>, and lambdas. The
constructors and combinators dispatch internally on
static_if (typeinfo can_copy(...)) and clone (or move) on the non-copyable
branch — call sites look identical to the workhorse-type case.
var arr <- [1, 2, 3]
let o = some(arr) // arr is cloned; arr is still [1, 2, 3]
let n = o |> map() $(v : array<int>) { return length(v); }
// n |> unwrap == 3
When you want to move a non-copyable payload into the option (instead of
cloning it), use move_some. After move_some(src) the source is left
empty:
var src <- [1, 2, 3]
let o = move_some(src) // src is now []
// o |> unwrap == [1, 2, 3]
Result provides the same pair on each side: ok / err clone, while
move_ok / move_err move:
var payload <- [4, 5, 6]
let r = move_ok(payload, type<string>) // payload is now []
// r |> unwrap == [4, 5, 6]
For workhorse types (int, float, bool, string, …) move_some /
move_ok / move_err are equivalent to their non-move siblings —
prefer the plain form for readability and use the move-variants only when
you genuinely want to drain a non-copyable source.
7.1.52.4. API reference at a glance
7.1.52.4.1. Option<T>
Function |
Meaning |
|---|---|
|
Wrap a value (clones non-copyable payload) |
|
Wrap a value by moving out of a mutable source |
|
Empty option of given payload type |
|
Tag queries |
|
Transform payload; |
|
Monadic bind |
|
Drop |
|
Eager fallback on |
|
Lazy fallback on |
|
Value or panic |
|
Value or eager default |
|
Value or computed default |
|
Value or |
|
Value or panic with custom message |
|
Side effect on |
|
Side effect on |
|
Pair two options; some iff both some |
|
Unwrap-or-default operator |
|
Structural equality |
|
True iff |
|
Negation of the above |
7.1.52.4.2. Result<T, E>
Function |
Meaning |
|---|---|
|
Wrap a success value (clones non-copyable) |
|
Wrap success by moving out of a mutable source |
|
Wrap an error (clones non-copyable) |
|
Wrap error by moving out of a mutable source |
|
Tag queries |
|
Transform ok payload |
|
Transform err payload |
|
Monadic bind on ok |
|
Monadic recovery on err |
|
Ok value or panic |
|
Ok value or eager default |
|
Ok value or computed-from-error default |
|
Ok value or |
|
Err value or panic |
|
Ok value or panic with custom message |
|
Err value or panic with custom message |
|
Side effect on either side |
|
Discard error; |
|
Discard value; |
|
Unwrap-or-default operator |
|
Structural equality |
|
True iff |
|
Negation of the above |
See also
Full source: tutorials/language/52_option_and_result.das
Previous tutorial: Delegates
Next tutorial: Command-Line Argument Parsing (clargs)