Skip to content

04 · Operators

Fallible Chaining

The ?> operator: chain calls that may fail, short-circuiting on error.

On this page

?> pipes a value into a fallible call and short-circuits if the result is an error — the fallible cousin of |>. It keeps happy-path chains flat instead of nesting match on every step:

Chaining fallible calls with ?>; the first error stops the chain.

12-fallible-chaining.zolo
Playground
// Feature: Fallible chaining operators — ?> (propagate+pipe), !. (unwrap+chain), ?. (null-safe)

// These three operators form a family: each handles Err/None differently when

// chaining method calls on Result or Option values.


fn doubled(n) { return n * 2 }

// `?>` — propagate-or-pipe.

// If the left side is Err/None, propagates out of the enclosing function

// (like `?`). If it is Ok/Some, pipes the unwrapped value into the RHS

// (like `|>`). Combines both operators in one step.

fn print_items() -> Result {
    Result.Ok([1, 2, 3]) ?> .each(|n| print(n))
    return Result.Ok(0)
}
let _ = print_items()
// expected: 1

// expected: 2

// expected: 3


// `!.` — force-unwrap then chain.

// Unwraps the Result/Option (panics on Err/None, like .unwrap()), then

// accesses the field or calls the method on the inner value.

let s = Result.Ok("hello")!.to_upper()    // unwrap, then call .to_upper()

print(doubled(s.len()))
// expected: 10


// `?.` — null-safe chain on an optional value.

// If the receiver is nil, short-circuits to nil instead of panicking.

// Combined with `??` it gives a safe default. `Option<T>` is a tagged

// wrapper, so unwrap/match it first; `?.` operates on the underlying `T?`.

struct Label { field: str }
let maybe: Label? = nil
print(maybe?.field ?? "none")
// expected: none


// `?>` is exactly `lhs? |> rhs`, even when nested inside another call.

// Moving this expression into or out of a `let` never changes which function

// receives the propagated error.

fn source(ok: bool) -> Result<int, str> {
    if ok { return Result.Ok(20) }
    return Result.Err("boom")
}
fn plus_one(value: int) -> int { value + 1 }
fn nested(ok: bool) -> Result<int, str> {
    return Result.Ok((source(ok) ?> plus_one) * 2)
}
match nested(true) {
    Result::Ok(value) => print(value),
    Result::Err(message) => print(message),
}
match nested(false) {
    Result::Ok(value) => print(value),
    Result::Err(message) => print(message),
}
// expected: 42

// expected: boom

Global index

Find your way through Zolo

Try an idea

Start here

9 results

9 results

en