moonbitlang/core/lazy does not have a README file

    Lazy

    type Lazy[A]

    A memoized thunk: the first call to force runs the thunk and caches the result; later calls return the cached value without re-running it.

    Construct one with Lazy(thunk) (deferred) or ready(value) (already evaluated). For fallible work, wrap the result in a Result value inside the thunk — failure as data is the recommended shape; see the rationale below.

    Why force is non-raising / non-async

    force has signature (Self[A]) -> A — no raise?, no async. That is a deliberate choice given how MoonBit surfaces effects in signatures:

    • No raise. A raising thunk would make force raise too, which would then leak into every consumer that touches a lazy cell — and any data structure built on top (lazy lists, lazy trees, memoized graph nodes) would inherit the effect at every traversal point. Memoizing the failure also forces a choice between "cache the exception and re-raise on every retry" (OCaml-style) and "retry on each force" (Rust-style); both are defensible but neither is obviously right. The recommended pattern for a fallible deferred computation is to make the failure data: Lazy(() => try? f()) produces a Lazy[Result[A, Error]], and the consumer handles the result at the call site it controls.

    • No async. Async memoization additionally needs an in-flight state to handle two coroutines racing to force the same cell, which would pull a concurrency primitive into a type whose only job is to delay a value. The right tool for an async deferred value is the language's promise/future type (which already gives "compute once, await many"), not a thunk wrapper.

    Concurrency

    Lazy[A] is not thread-safe. Sharing one across threads requires external synchronization.
    impl Debug for Lazy[A]

    Lazy::Lazy

    fn[A] Lazy::Lazy(thunk : () -> A) -> Lazy[A]

    Wraps a thunk. The thunk is not invoked until force is called; on the first force, its result is memoized.

    test {
    let mut runs = 0
    let cell = @lazy.Lazy(() => {
    runs 1
    42
    })
    @test.assert_eq(cell.force(), 42)
    @test.assert_eq(cell.force(), 42)
    @test.assert_eq(runs, 1)
    }

    Lazy::force

    fn[A] Lazy::force(self : Lazy[A]) -> A

    Returns the cell's value, running the thunk on the first call and caching the result. Later calls return the cached value in O(1).

    Reentrant forces (a thunk that, while running, forces the same cell again — e.g., through a captured Ref) are detected and aborted. Without that check, the inner force would silently re-run the thunk and break the at-most-once memoization guarantee.

    Lazy::peek

    fn[A] Lazy::peek(self : Lazy[A]) -> A?

    Returns the cached value if the cell has already been forced, or None if the thunk has not yet run (or is currently running). Never invokes the thunk — safe to call on cells whose thunks are infinite, effectful, or expensive.

    Useful for introspection / debugging of lazy data structures: you can walk a chain of Lazy cells and render only the parts that are already evaluated.

    test {
    let cell = @lazy.Lazy(() => 7)
    debug_inspect(cell.peek(), content="None")
    ignore(cell.force())
    debug_inspect(cell.peek(), content="Some(7)")
    }

    Lazy::ready

    fn[A] Lazy::ready(value : A) -> Lazy[A]

    Wraps an already-evaluated value. force returns it directly without running any code.

    test {
    let cell = @lazy.Lazy::ready(7)
    @test.assert_eq(cell.force(), 7)
    }