What operations and callbacks does Result support?

← Getting Started · Ref: Q87

Result supports callbacks, factory methods, operators for comparison, copy, merge, and conversions beyond basic guard access.

WHEN OK / WHEN ERROR CALLBACKS

  r.whenOk(myConsumer)           called only if ok present
  r.whenError(errorConsumer)     called only if error present

Consumer is pure. Acceptor is non-pure (can mutate/do I/O). Callback never invoked if value absent.

CONTAINS

  r contains "Steve"             true if isOk AND ok equals argument

ITERATOR

Yields 0 or 1 elements over the ok value:

  while iter <- r.iterator() then iter.hasNext()
    item :=: iter.next()

FACTORY METHODS

  justOk <- r.asOk("Steve")     ok only
  justErr <- r.asError(-1)       error only
  cleared <- r.asEmpty()         neither set

MERGE (:~:)

Fills unset sides from another Result without overwriting:

  r1 :~: r2

Ideal for layering defaults.

COPY (:=:), COMPARISON (== <>)

Deep copy: target :=: source. Two empty Results are equal.

STRING ($), JSON ($$), HASHCODE (#?)

  $r, $$r, #? r

See Q48 for Result basics. See Q86 for Result guards. See Q54 for Consumer/Acceptor. See Q29 for tri-state. See Q139 for Result vs try/catch. See Q259 for four-state design.

Example

defines module qa.result.operations

  defines function

    <?-
      Returns a Result with ok value set.
    -?>
    getOkResult()
      <- rtn <- Result("Steve", Integer())

    <?-
      Pure consumer for ok values.
    -?>
    okProcessor() as pure
      -> okContent as String
      require okContent?

    <?-
      Pure consumer for error values.
    -?>
    errorProcessor() as pure
      -> code as Integer
      require code?

  defines program
    ResultOperationsDemo()
      stdout <- Stdout()

      // === WHEN OK / WHEN ERROR CALLBACKS ===

      // Pure Consumer callbacks — read-only
      okOnly <- Result("Steve", Integer())
      okOnly.whenOk(okProcessor)
      stdout.println("whenOk with Consumer: called on ok Result")

      errOnly <- Result(String(), -1)
      errOnly.whenError(errorProcessor)
      stdout.println("whenError with Consumer: called on error Result")

      // Callbacks on Result with both values
      both <- Result("Default", -1)
      both.whenOk(okProcessor)
      both.whenError(errorProcessor)
      stdout.println("Both callbacks fired on dual-value Result")

      // Callback NOT fired when value absent
      emptyResult <- Result() of (String, Integer)
      emptyResult.whenOk(okProcessor)
      emptyResult.whenError(errorProcessor)
      stdout.println("Empty Result: neither callback fires")

      // === CONTAINS ===

      stdout.println(`contains Steve: ${okOnly contains "Steve"}`)
      stdout.println(`contains Other: ${okOnly contains "Other"}`)
      stdout.println(`error contains: ${errOnly contains "Steve"}`)

      // === ITERATOR ===

      extracted <- String()
      while iter <- okOnly.iterator() then iter.hasNext()
        extracted :=: iter.next()
      stdout.println(`Iterator ok: ${extracted}`)

      // Empty Result iterator yields nothing
      noItems <- String()
      while iter <- emptyResult.iterator() then iter.hasNext()
        noItems :=: iter.next()
      stdout.println(`Iterator empty: ${noItems?}`)

      // === FACTORY METHODS ===

      prototype <- Result("Proto", 99)

      justOk <- prototype.asOk("Fresh")
      require justOk.isOk() and not justOk.isError()
      stdout.println(`asOk: ${justOk}`)

      justErr <- prototype.asError(42)
      require not justErr.isOk() and justErr.isError()
      stdout.println(`asError: ${justErr}`)

      cleared <- prototype.asEmpty()
      require cleared is empty
      stdout.println(`asEmpty: ${cleared}`)

      // === MERGE (:~:) ===

      m1 <- Result("Steve", Integer())
      m2 <- Result(String(), -1)
      m1 :~: m2
      require m1.isOk() and m1.isError()
      stdout.println(`Merged: ${m1}`)

      // Merge does not overwrite existing ok
      m3 <- Result("Keep", Integer())
      m4 <- Result("Overwrite", -2)
      m3 :~: m4
      if m3.isOk()
        stdout.println(`Merge kept ok: ${m3.ok()}`)

      // === COPY (:=:) ===

      original <- Result("Alice", 7)
      copied <- Result() of (String, Integer)
      copied :=: original
      require copied == original
      stdout.println(`Copied: ${copied}`)

      // === COMPARISON (== and <>) ===

      c1 <- Result("A", 1)
      c2 <- Result("A", 1)
      c3 <- Result("B", 2)
      stdout.println(`c1 == c2: ${c1 == c2}`)
      stdout.println(`c1 <> c3: ${c1 <> c3}`)

      // Empty Results are equal
      e1 <- Result() of (String, Integer)
      e2 <- Result() of (String, Integer)
      stdout.println(`empty == empty: ${e1 == e2}`)

      // === STRING ($), JSON ($$), HASHCODE (#?) ===

      display <- Result("Steve", 42)
      stdout.println(`String: ${display}`)
      stdout.println(`JSON: ${$ display}`)
      stdout.println(`Hash: ${#? display}`)

Common mistakes

E08130 — Calling a non-pure function like getOkResult() from within a pure function triggers E08130 — not marked pure but call is made in a pure scope. Pure functions cannot call non-pure functions. See ek9 -h E08130 for details.

Incorrect:

okProcessor() as pure
      -> okContent as String
      result <- getOkResult()
      require okContent?

Correct:

okProcessor() as pure
      -> okContent as String
      require okContent?

E06190 — Result requires two different types for ok and error. Using the same type for both triggers E06190 — Result must be used with two different types. This ensures the compiler can distinguish ok from error. See ek9 -h E06190 for details.

Incorrect:

Result("Steve", String())

Correct:

Result("Steve", Integer())
Other ways to ask this
  • How do whenOk and whenError callbacks work on Result?
  • How do I compare or copy Result values in EK9?
  • How does the merge operator work with Result?

Coming from another language?

Java: no Result, CompletableFuture callbacks no compile safety. Rust: map/and_then, no merge, either/or only. Go: (value, error) tuple, manual checks. EK9: whenOk/whenError callbacks, :~: merge, :=: copy, contains, iterator, factory methods, compile-time safety.

Keywords: copy, asEmpty, whenError, contains, whenOk, json, guard, result, error, callback, acceptor, ok, asOk, string, operations, iterator, start, asError, intro, first, hashcode, comparison, consumer, merge, beginner