Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Core

print, println, and log produce a null event after processing each message. This acknowledgement lets a seq step advance, including when the same message is printed again. Changing only the destination produces no acknowledgement. If an enclosing expression should produce nothing, end its block with never() explicitly.

type Sint = [ i8, i16, i32, z32, i64, z64 ];
type Uint = [ u8, u16, u32, v32, u64, v64 ];
type Int = [ Sint, Uint ];
type Float = [ f32, f64 ];
type Real = [ Float, decimal ];
type Number = [ Int, Real ];
type NotNull = [Number, string, error, array, datetime, duration, bytes, bool];
type Primitive = [NotNull, null];
type PrimNoErr = [Number, string, bool, array, datetime, duration, bytes, null];
type Log = [`Trace, `Debug, `Info, `Warn, `Error, `Stdout, `Stderr];
type Result<'r: NoError, 'e> = ['r, Error<'e>];
type Option<'a> = ['a, null];

/// The result of a three-way comparison.
type Ordering = [`Less, `Equal, `Greater];

/// Equality. `==` and `!=` consult the implementation of the operand
/// type — and of every type nested inside it — falling back to the
/// structural comparison of values where there is none. Outside core
/// only an abstract type may implement it. The method is implicitly
/// `#[sync]`: it runs inside the comparison.
trait Eq {
    /// true if the two values are equal
    val eq: fn(self, other: self) -> bool
};

/// Ordering. `<`, `>`, `<=` and `>=` consult the implementation the
/// way `==` consults `Eq`, and so do `array::sort`, `min`/`max` and map
/// keys. Implicitly `#[sync]`.
trait Ord {
    /// the order of `self` relative to `other`
    val cmp: fn(self, other: self) -> Ordering
};

/// Printing. String interpolation, `print`/`println`, `dbg` and `log`
/// consult the implementation the way `==` consults `Eq`, everywhere
/// in the printed type. Implicitly `#[sync]`.
trait Display {
    /// the printed form of `self`
    val fmt: fn(self) -> string
};

/// Data. An abstract type crosses data only through its implementation:
/// a cast or a typed read (`json::read`, `pack::read`,
/// `sys::net::subscribe`, ..) makes a value with `from_value`, and a
/// writer (`json::write`, `pack::write`, `sys::net::publish`, ..)
/// writes what `to_value` returns. Only the module that defines the
/// type, where its representation is visible, implements it, and a
/// method it leaves out is the representation itself: `impl Cast for T
/// {}` reads and writes `T` as its representation. Casts and writers
/// call the methods, a program never does. Implicitly `#[sync]`.
trait Cast {
    /// the data `self` is written as
    val to_value: fn(self) -> Any;
    /// the value `v` reads as, or why it can't
    val from_value: fn(v: Any) -> Result<self, `InvalidCast(string)>
};

/// A collection: a type constructor whose values can be traversed and
/// rebuilt — `Array`, `List`, `Map` (over its values), and any type a
/// program implements it for. `self<'a>` is the constructor applied to
/// the element type. `fold`, `filter_map` and `flat_map` are required;
/// the rest derive from them, and an implementation overrides what it
/// can do better. Over an element type that holds null, the derived
/// `map` and `filter` drop the null elements (they go through
/// `filter_map`'s `Option`); the built-in collections override both.
/// `use Collection::*` makes the methods callable bare, and `map(c, f)`
/// then does the same thing whatever `c` is. A parameter typed
/// `Collection` accepts any collection: `|c: Collection| fold(c, ..)`.
trait Collection {
    /// fold the elements, front to back
    val fold: fn(self<'a>, init: 'b, f: fn(acc: 'b, x: 'a) -> 'b throws 'e) -> 'b throws 'e;
    /// the elements `f` maps to a value, in order
    val filter_map: fn(self<'a>, f: fn(x: 'a) -> Option<'b> throws 'e) -> self<'b> throws 'e;
    /// the concatenation of `f` over the elements
    val flat_map: fn(self<'a>, f: fn(x: 'a) -> self<'b> throws 'e) -> self<'b> throws 'e;
    // CR claude for eric: [risk/low] The derived map and filter go through filter_map's
    // Option, so a null result of f (map) or a null element (filter) is dropped. On a
    // program-defined collection, map(c, |x| x) and filter(c, |_| true) therefore lose
    // elements without any warning. Example: a cons list L<[i64, null]> of 1, null, 3
    // gives (len, len of map, len of filter) = (3, 2, 2). The trait doc attributes this
    // to the element type, but for map it is f's result type. Either make map and
    // filter required (no defaults), or give filter_map a tag that cannot collide with
    // element values (`Keep('b) / `Drop) so the derivations are total. At minimum,
    // correct the doc. probe: design/review-2026-10-12/repro/gx-stdlib-05.gx
    // (gx-stdlib-06)
    /// `f` over every element
    val map: fn(self<'a>, f: fn(x: 'a) -> 'b throws 'e) -> self<'b> throws 'e
        = |c, f| filter_map(c, |x| f(x));
    /// the elements `f` accepts
    val filter: fn(self<'a>, f: fn(x: 'a) -> bool throws 'e) -> self<'a> throws 'e
        = |c, f| filter_map(c, |x| select f(x) { true => x, false => null });
    /// the first element `f` accepts
    val find: fn(self<'a>, f: fn(x: 'a) -> bool throws 'e) -> Option<'a> throws 'e
        = |c, f| {
            let init: [`Found('a), null] = null;
            let found = fold(c, init, |acc, x| select acc {
                null as _ => select f(x) { true => `Found(x), false => null },
                found => found
            });
            select found { `Found(x) => x, null as _ => null }
        };
    /// the first value `f` maps an element to
    val find_map: fn(self<'a>, f: fn(x: 'a) -> Option<'b> throws 'e) -> Option<'b> throws 'e
        = |c, f| {
            let init: Option<'b> = null;
            fold(c, init, |acc, x| select acc { null as _ => f(x), found => found })
        };
    /// the number of elements
    val len: fn(self<'a>) -> i64 = |c| fold(c, 0, |n, _| n + 1)
};

impl Collection for Array<'_>;
impl<'k> Collection for Map<'k, '_>;
impl Collection for List<'_>;

type Pos = {
    line: i32,
    column: i32
};

type Source = [
    `File(string),
    `Netidx(string),
    `Vfs(string),
    `Program,
    `Unspecified
];

type Ori = {
    parent: [Ori, null],
    source: Source,
    text: string
};

type ErrChain<'a> = {
    cause: [ErrChain<'a>, null],
    error: 'a,
    ori: Ori,
    pos: Pos
};

/// return the first argument when all arguments are equal, otherwise return nothing
val all: fn<'a: Discernible>(a: 'a, @args: 'a) -> 'a;

/// return true if all arguments are true, otherwise return false
val and: fn(@args: bool) -> bool;

/// return the number of times x has updated
val count: fn(x: Any) -> i64;

/// return the first argument divided by all subsequent arguments; a
/// failed division (by zero, an overflow) is logged and returns nothing
val divide: fn(@args: [Number, Array<[Number, Array<Number>]>]) -> Number;

/// return e only if e is an error
val filter_err: fn(e: Result<'a, 'b>) -> Error<'b>;

/// return v if f(v) is true, otherwise return nothing
val filter: fn(v: 'a, f: fn(v: 'a) -> bool throws 'e) -> 'a throws 'e;

/// return true if e is an error
val is_err: fn(e: Any) -> bool;

/// construct an error carrying e
val error: fn(e: 'a) -> Error<'a>;

/// return the maximum value of any argument
val max: fn<'a: Ordered>(a: 'a, @args: 'a) -> 'a;

/// return the mean of the passed in arguments
val mean: fn(v: [Number, Array<Number>], @args: [Number, Array<Number>]) -> Result<f64, `MeanError(string)>;

/// return the minimum value of any argument
val min: fn<'a: Ordered>(a: 'a, @args:'a) -> 'a;

/// return v only once, subsuquent updates to v will be ignored
/// and once will return nothing
val once: fn(v: 'a) -> 'a;

/// take n updates from e and drop the rest; a negative n takes none. The
/// count restarts when n updates.
val take: fn(#n: i64, e: 'a) -> 'a;

/// skip n updates from e and return the rest; a negative n skips none.
/// The count restarts when n updates.
val skip: fn(#n: i64, e: 'a) -> 'a;

/// range will update j - i times, starting at i and ending at j - 1
val range: fn(i: i64, j: i64) -> Result<i64, `RangeError(string)>;

/// return true if any argument is true
val or: fn(@args: bool) -> bool;

/// bitwise AND
val bit_and: fn<'a: Int + Singleton>(a: 'a, b: 'a) -> 'a;

/// bitwise OR
val bit_or: fn<'a: Int + Singleton>(a: 'a, b: 'a) -> 'a;

/// bitwise XOR
val bit_xor: fn<'a: Int + Singleton>(a: 'a, b: 'a) -> 'a;

/// bitwise complement
val bit_not: fn<'a: Int>(a: 'a) -> 'a;

/// shift left (wrapping)
val shl: fn<'a: Int + Singleton>(a: 'a, b: 'a) -> 'a;

/// shift right (wrapping)
val shr: fn<'a: Int + Singleton>(a: 'a, b: 'a) -> 'a;

/// return the product of all arguments
val product: fn(@args: [Number, Array<[Number, Array<Number>]>]) -> Number;

/// return the sum of all arguments
val sum: fn(@args: [Number, Array<[Number, Array<Number>]>]) -> Number;

/// when v updates return v if the new value is different from the previous value,
/// otherwise return nothing.
val uniq: fn<'a: Discernible>(v: 'a) -> 'a;

/// when v updates place it's value in an internal fifo queue. when clock updates
/// return the oldest value from the fifo queue. If clock updates and the queue is
/// empty, record the number of clock updates, and produce that number of
/// values from the queue when they are available. When flush updates
/// discard every value waiting in the queue; recorded clock updates are kept.
/// When flush_before updates to n, discard the values waiting that arrived
/// before the n-th v update, the first being the 0-th.
val queue: fn(#clock:Any, ?#flush:Any, ?#flush_before: [i64, null], v: 'a) -> 'a;

/// queuefn wraps a function f and returns a function with the same signature
/// whose invocations are queued and released by an external trigger.
///
/// Each call site of queuefn creates a fresh queue. The wrapper has an
/// internal pop_count initialized to 1, so the first invocation runs
/// immediately. Subsequent invocations push their args onto the queue and
/// emit nothing until #trigger fires; each #trigger update releases one
/// queued invocation, or banks a pop slot if the queue is empty.
///
/// If #count is provided as a writable ref, the current queue depth is
/// written through it whenever it changes, to a place (`&mut s.depth`)
/// as to a binding: as a level, so a cycle's changes arrive as its final
/// depth. If null (default), depth is not reported. f may not
/// have a variadic argument or a defaulted label: wrap a lambda that
/// calls it.
///
/// When f re-emits, the queue and pop_count are NOT reset. Subsequent
/// pops process queued args with the new f, regardless of which f was
/// current when those args were queued. If you need a fresh queue when f
/// changes, build it explicitly using `queue` instead.
///
/// Concurrent in-flight invocations are possible if #trigger fires before
/// f finishes emitting a previous invocation's output — the wrapper
/// emits whatever any in-flight invocation produces.
val queuefn: fn<'a: Function>(?#count: [&mut i64, null], #trigger:Any, f: 'a) -> 'a;

/// hold the most recent value of v interally until clock updates. If v updates
/// more than once before clock updates, older values of v will be discarded,
/// only the most recent value will be retained. If clock updates when no v is held
/// internall, record the number of times it updated, and pass that many v updates
/// through immediatly when they happen.
val hold: fn(#clock:Any, v: 'a) -> 'a;


/// when v updates, return it, but also print it along
/// with the position of the expression to the specified sink
val dbg: fn(?#dest:[`Stdout, `Stderr, Log], v: 'a) -> 'a;

/// print a log message to stdout, stderr or the specified log level using the rust log
/// crate. Returns null after processing each message.
val log: fn(?#dest:Log, msg: 'a) -> null;

/// print a raw value to stdout, stderr or the specified log level using the rust log
/// crate. Does not automatically insert a newline or add the source module/location.
/// Returns null after processing each message.
val print: fn(?#dest:Log, msg: 'a) -> null;

/// print a raw value to stdout, stderr or the specified log level using the rust log
/// crate followed by a newline. Returns null after processing each message.
val println: fn(?#dest:Log, msg: 'a) -> null;

/// Throttle v so it updates at most every #rate, where rate is a
/// duration (default 0.5 seconds). Intermediate updates that push v
/// over the #rate will be discarded. The most recent update will always
/// be delivered. If the sequence, m0, m1, ..., mN, arrives simultaneously
/// after a period of silence, first m0 will be delivered, then after the rate
/// timer expires mN will be delivered, m1, ..., m(N-1) will be discarded.
val throttle: fn(?#rate:duration, v: 'a) -> 'a;


mod buffer;
mod math;
mod opt;

core::buffer

The buffer submodule provides functions for working with raw bytes: conversion between bytes and strings/arrays, concatenation, and a flexible binary encode/decode system with control over endianness and variable-length encoding.

use super::Result;
/// Convert bytes to a UTF-8 string.
val to_string: fn(b: bytes) -> Result<string, `EncodingError(string)>;

/// Convert bytes to a UTF-8 string, replacing invalid sequences.
val to_string_lossy: fn(b: bytes) -> string;

/// Convert a string to its UTF-8 bytes.
val from_string: fn(s: string) -> bytes;

/// Concatenate bytes values.
val concat: fn(@args: [bytes, Array<bytes>]) -> bytes;

/// Convert bytes to an Array<u8>.
val to_array: fn(b: bytes) -> Array<u8>;

/// Convert an Array<u8> to bytes.
val from_array: fn(a: Array<u8>) -> bytes;

/// Return the length of a bytes value.
val len: fn(b: bytes) -> u64;

/// Spec for encoding values into bytes. Bare tags are
/// big-endian (network byte order), LE suffix for little-endian.
type Encode = [
  `I8(i8), `U8(u8),
  `I16(i16), `I16LE(i16), `U16(u16), `U16LE(u16),
  `I32(i32), `I32LE(i32), `U32(u32), `U32LE(u32),
  `I64(i64), `I64LE(i64), `U64(u64), `U64LE(u64),
  `F32(f32), `F32LE(f32), `F64(f64), `F64LE(f64),
  `Bytes(bytes),
  `Pad(u64),
  `Varint(u64),
  `Zigzag(i64)
];

/// Spec for decoding bytes into refs. Bare tags are
/// big-endian (network byte order), LE suffix for little-endian.
/// Each decoded field is written through its &mut. Variable-length
/// fields take a &u64 for the length so that
/// earlier decoded lengths can be resolved within the same call.
type Decode = [
  `I8(&mut i8), `U8(&mut u8),
  `I16(&mut i16), `I16LE(&mut i16), `U16(&mut u16), `U16LE(&mut u16),
  `I32(&mut i32), `I32LE(&mut i32), `U32(&mut u32), `U32LE(&mut u32),
  `I64(&mut i64), `I64LE(&mut i64), `U64(&mut u64), `U64LE(&mut u64),
  `F32(&mut f32), `F32LE(&mut f32), `F64(&mut f64), `F64LE(&mut f64),
  `Bytes(&u64, &mut bytes),
  `UTF8(&u64, &mut string),
  `Skip(&u64),
  `Varint(&mut u64),
  `Zigzag(&mut i64)
];

/// Encode values into bytes according to the spec.
val encode: fn(spec: Array<Encode>) -> bytes;

/// Decode bytes into refs according to the spec.
/// Returns the remaining bytes after all fields are consumed.
val decode: fn(buf: bytes, spec: Array<Decode>) -> Result<bytes, `DecodeError(string)>;

core::math

The math submodule wraps Rust’s f64 math intrinsics (trigonometric, hyperbolic, exponential, logarithmic, power, rounding, comparison, predicate, and angle-conversion routines) plus the standard mathematical constants. Argument and result conventions match std::f64: angles are in radians, NaN propagates through arithmetic, and min/max return the non-NaN operand when one input is NaN.

For polymorphic n-ary min / max / sum / product over Number, use the top-level functions in core instead — the bindings here are the binary f64-only forms.

/// Sine. Argument in radians.
val sin: fn(x: f64) -> f64;

/// Cosine. Argument in radians.
val cos: fn(x: f64) -> f64;

/// Tangent. Argument in radians.
val tan: fn(x: f64) -> f64;

/// Inverse sine. Result in radians, [-π/2, π/2].
val asin: fn(x: f64) -> f64;

/// Inverse cosine. Result in radians, [0, π].
val acos: fn(x: f64) -> f64;

/// Inverse tangent. Result in radians, (-π/2, π/2).
val atan: fn(x: f64) -> f64;

/// Four-quadrant inverse tangent: `atan2(y, x)`. Result in radians, [-π, π]
/// (`atan2(-0.0, -1.0)` is -π).
val atan2: fn(y: f64, x: f64) -> f64;

/// Hyperbolic sine.
val sinh: fn(x: f64) -> f64;

/// Hyperbolic cosine.
val cosh: fn(x: f64) -> f64;

/// Hyperbolic tangent.
val tanh: fn(x: f64) -> f64;

/// Inverse hyperbolic sine.
val asinh: fn(x: f64) -> f64;

/// Inverse hyperbolic cosine.
val acosh: fn(x: f64) -> f64;

/// Inverse hyperbolic tangent.
val atanh: fn(x: f64) -> f64;

/// `e^x`.
val exp: fn(x: f64) -> f64;

/// `2^x`.
val exp2: fn(x: f64) -> f64;

/// `e^x - 1`. More accurate than `exp(x) - 1` near zero.
val exp_m1: fn(x: f64) -> f64;

/// Natural logarithm (base e).
val ln: fn(x: f64) -> f64;

/// `ln(1 + x)`. More accurate than `ln(1 + x)` near zero.
val ln_1p: fn(x: f64) -> f64;

/// Logarithm base 2.
val log2: fn(x: f64) -> f64;

/// Logarithm base 10.
val log10: fn(x: f64) -> f64;

/// Logarithm with arbitrary base: `log(x, base) = ln(x) / ln(base)`.
val log: fn(x: f64, base: f64) -> f64;

/// `x^y`.
val pow: fn(x: f64, y: f64) -> f64;

/// Square root.
val sqrt: fn(x: f64) -> f64;

/// Cube root.
val cbrt: fn(x: f64) -> f64;

/// `sqrt(x^2 + y^2)`, computed without overflow for large inputs.
val hypot: fn(x: f64, y: f64) -> f64;

/// Largest integer less than or equal to `x`.
val floor: fn(x: f64) -> f64;

/// Smallest integer greater than or equal to `x`.
val ceil: fn(x: f64) -> f64;

/// Round to the nearest integer, ties away from zero.
val round: fn(x: f64) -> f64;

/// Truncate toward zero.
val trunc: fn(x: f64) -> f64;

/// Fractional part: `x - trunc(x)`.
val fract: fn(x: f64) -> f64;

/// Absolute value.
val abs: fn(x: f64) -> f64;

/// Sign: 1.0 when `x` is positive or `0.0`, -1.0 when it is negative or
/// `-0.0`. NaN if `x` is NaN.
val signum: fn(x: f64) -> f64;

/// `x` with the sign of `y`.
val copysign: fn(x: f64, y: f64) -> f64;

/// Smaller of two f64 values, returning the non-NaN operand if one is NaN.
/// Core `min` takes any number of `'a: Ordered` values and follows the
/// language's total order, where NaN is below every number: `min(math::nan,
/// 1.0)` is NaN where `math::min` gives 1.
val min: fn(x: f64, y: f64) -> f64;

/// Larger of two f64 values, returning the non-NaN operand if one is NaN.
/// Core `max` takes any number of `'a: Ordered` values and follows the
/// language's total order, where NaN is below every number.
val max: fn(x: f64, y: f64) -> f64;

/// Clamp `x` to the closed interval `[lo, hi]`. Returns a catchable
/// `` `ClampError `` when the range is invalid (`lo > hi`, or a NaN bound)
/// — handle it with `?`/`$`/`select`.
val clamp: fn(x: f64, lo: f64, hi: f64) -> [f64, Error<`ClampError(string)>];

/// True if `x` is NaN.
val is_nan: fn(x: f64) -> bool;

/// True if `x` is finite (not NaN, not infinite).
val is_finite: fn(x: f64) -> bool;

/// True if `x` is positive or negative infinity.
val is_infinite: fn(x: f64) -> bool;

/// Convert radians to degrees.
val to_degrees: fn(x: f64) -> f64;

/// Convert degrees to radians.
val to_radians: fn(x: f64) -> f64;

/// π ≈ 3.14159265358979…
val pi: f64;

/// e ≈ 2.71828182845904…
val e: f64;

/// τ = 2π ≈ 6.28318530717958…
val tau: f64;

/// √2 ≈ 1.41421356237309…
val sqrt_2: f64;

/// ln(2) ≈ 0.69314718055994…
val ln_2: f64;

/// ln(10) ≈ 2.30258509299404…
val ln_10: f64;

/// Positive infinity.
val infinity: f64;

/// Not-a-number.
val nan: f64;

core::opt

The opt submodule provides combinators for working with optional values — graphix’s Option<'a> is just the structural union ['a, null], and these functions mirror the most useful parts of Rust’s std::option::Option API in a reactive setting.

The plain combinators (contains, or, and, zip, …) are strict, like every function and like && and ||: they produce once every argument has a value, so opt::or(a, b) waits for b even when a is not null. To decide from the first alone, select on it: select a { null => b, v => v }.

The higher-order combinators (map, flat_map, filter, or_else, ok_or_else, is_some_and, is_none_or) are deliberately fire-and-forget: they never queue inputs. If a callback is slow or never produces a value, a new input simply supersedes the pending one (latest wins). Use the explicit core::queue / core::hold operators when you need ordered async behavior.

use super::Result;
/// true if v is not null
val is_some: fn(v: ['a, null]) -> bool;

/// true if v is null
val is_none: fn(v: ['a, null]) -> bool;

/// true if v is not null and equals x. Produces false when v is null,
/// whatever x is.
val contains: fn<'a: Discernible>(v: ['a, null], x: 'a) -> bool;

/// or_default(v, d): if v is null return d, otherwise return v
val or_default: fn(v: ['a, null], d: 'a) -> 'a;

/// or(a, b): return a if non-null, otherwise return b (which may itself
/// be null).
val or: fn(a: ['a, null], b: ['a, null]) -> ['a, null];

/// and(a, b): return b if a is non-null, otherwise return null.
val and: fn(a: ['a, null], b: ['b, null]) -> ['b, null];

/// xor(a, b): return whichever of a or b is non-null, or null if both
/// or neither are non-null.
val xor: fn(a: ['a, null], b: ['a, null]) -> ['a, null];

/// ok_or(v, e): return v unchanged when non-null, otherwise return
/// error(e).
val ok_or: fn(v: ['a, null], e: 'e) -> Result<'a, 'e>;

/// zip(a, b): if both are non-null return the tuple (a, b), otherwise
/// return null.
val zip: fn(a: ['a, null], b: ['b, null]) -> [('a, 'b), null];

/// unzip(p): given a tuple-valued option, return the pair of options.
/// null input yields (null, null).
val unzip: fn(p: [('a, 'b), null]) -> (['a, null], ['b, null]);

/// map(v, f): apply f to the inner value if v is non-null, otherwise
/// return null. It is `select v { null as _ => null, x => f(x) }`, so it
/// fires as f does: f answers every non-null input (a slow async f
/// answers each in order), and fires again whenever what f reads fires.
val map: fn(v: ['a, null], f: fn(x: 'a) -> 'b throws 'e) -> ['b, null] throws 'e;

/// flat_map(v, f): apply f to the inner value if v is non-null,
/// otherwise return null. f's own optional result is forwarded
/// unchanged. It fires as map does.
val flat_map: fn(v: ['a, null], f: fn(x: 'a) -> ['b, null] throws 'e) -> ['b, null] throws 'e;

/// filter(v, pred): if v is non-null and pred(v) is true, emit v.
/// Otherwise (v is null, or pred is false) emit null. It fires as map
/// does.
///
/// Caveat for reactive predicates: when pred takes more than one cycle
/// to produce its bool, the emitted value is the *current* input, not
/// the input pred was actually answering for. If the input changes
/// between feeding pred and pred firing, you'll see the new value
/// gated on a verdict about the old one. Pure predicates (the
/// expected case) are unaffected. Use `core::queue` if you need
/// strict input-bool pairing.
val filter: fn(v: ['a, null], f: fn(x: 'a) -> bool throws 'e) -> ['a, null] throws 'e;

/// or_else(v, f): return v if non-null, otherwise return whatever f()
/// produces. f is invoked eagerly and its latest value is cached so
/// later updates to v can be resolved without re-invoking f. If v is
/// null before f has produced its first value, no output is emitted
/// until f fires.
val or_else: fn(v: ['a, null], f: fn() -> ['a, null] throws 'e) -> ['a, null] throws 'e;

/// ok_or_else(v, f): return v if non-null, otherwise return
/// error(f()). Same eager-with-caching semantics as or_else; null v
/// before f's first firing is silent until f produces.
val ok_or_else: fn(v: ['a, null], f: fn() -> 'e throws 'x) -> Result<'a, 'e> throws 'x;

/// is_some_and(v, pred): true when v is non-null and pred(v) is true,
/// false when v is null or pred is false. It fires as map does: for a
/// non-null v, no output is produced until pred fires.
val is_some_and: fn(v: ['a, null], f: fn(x: 'a) -> bool throws 'e) -> bool throws 'e;

/// is_none_or(v, pred): true when v is null or pred(v) is true, false
/// when v is non-null and pred is false. It fires as map does: for a
/// non-null v, no output is produced until pred fires.
val is_none_or: fn(v: ['a, null], f: fn(x: 'a) -> bool throws 'e) -> bool throws 'e