option
stdlib/option.hk: Option<T> and its helpers.
Option<T> is the canonical "value or absence" sum from HANKI.md §4. Pure Hanki: the sum-type machinery and the variant-ctor generics work do the heavy lifting; map rides on the higher-order call lowering so it can invoke its fn-typed callback.
Methods land on impl<T> Option<T> ... end; call as opt.unwrap_or(default), opt.map(|v| ...).
Option
type Option<T>
Some(T)
None
end
impl<T> Option<T>
unwrap_or
def unwrap_or(self, default: T) -> T
Returns the wrapped value, or default when the option is None.
Some(7i32).unwrap_or(0i32) => 7i32
n: Option<i32> = None
n.unwrap_or(9i32) => 9i32
or_crash!
def or_crash!(self, msg: string) -> T [Crash]
Returns the wrapped value, or crash!es with msg when the option is None, killing the current actor, and at the program root exiting 252 after printing crash: <msg> (HANKI.md §15). For a script that has nothing useful to do with an absence and would otherwise write the same four-line match at every call: xs.first.or_crash!("empty argument list").
The message is required and is not synthesised: None reports no detail of its own, and whatever the reader needs at the crash has to come from the caller.
This is the one partial method on Option, and it shares [Crash] with Result.or_crash! alone among stdlib APIs. Both are intended and neither is a precedent: every other method here is total, and a caller who wants to handle the absence should still match, unwrap_or, or or_else. [Crash] is viral (H0601: every caller up the chain must declare it) and no pure def can call it at all (H0606), and this is unusable from library code that means to stay pure, and free in a script whose main! is already an action. That asymmetry is the design.
Some(7i32).or_crash!("unreachable — the option is Some") => 7i32
map
def map(self, f: (T) -> U) -> Option<U>
Applies f to the wrapped value, or propagates None untouched.
Some(3i32).map(|x| x * 2i32) => Some(6i32)
map!
def map!(self, f: (T) -> U [e]) -> Option<U> [e]
map with an effectful transform. f runs only on Some, and a None costs nothing and performs nothing.
Some(3i32).map!(|x: i32| x * 2i32) => Some(6i32)
and_then
def and_then(self, f: (T) -> Option<U>) -> Option<U>
Chains a fallible step: it applies f, which itself yields an Option, to the wrapped value, or propagates None. The flattening counterpart of map, and a chain of fallible lookups therefore nests no Options.
Some(3i32).and_then(|x| Some(x * 2i32)) => Some(6i32)
and_then!
def and_then!(self, f: (T) -> Option<U> [e]) -> Option<U> [e]
and_then with an effectful step, the effectful lookup chain, where each step both performs and may come up empty. f runs only on Some.
Some(3i32).and_then!(|x: i32| Some(x * 2i32)) => Some(6i32)
or_else
def or_else(self, f: () -> Option<T>) -> Option<T>
Falls back where the option is empty: it leaves a Some untouched and otherwise returns the option f produces. It is lazy: f runs only on None, and fallback chains therefore read a().or_else(|| b()).or_else(|| c()).
n: Option<i32> = None
n.or_else(|| Some(9i32)) => Some(9i32)
or_else!
def or_else!(self, f: () -> Option<T> [e]) -> Option<T> [e]
or_else with an effectful fallback, for "look it up cheaply, and only where that misses go and fetch it". Lazy like the pure form, and a Some performs nothing at all.
n: Option<i32> = None
n.or_else!(|| Some(9i32)) => Some(9i32)
impl<T: Display> Display<Option<T>>
to_string
def to_string(self) -> string
Some(inner), the payload through its own Display, or None. Hand-written, though the auto-derive could produce it: synthesis is demanded only at the type a #{…} names, and an Option reached as a payload is never itself demanded and needs a standing impl. (For List and Map the reason is the other one: both are opaque intrinsics with no variants for a derive to walk.)
Some(7i32).to_string() => "Some(7)"
n: Option<i32> = None
n.to_string() => "None"