How do I process an Optional or Result value using callbacks?

← Safe Value Access · Ref: Q169

EK9 Optional and Result support callback-based processing using whenPresent, whenOk, and whenError. These accept Consumer (pure, read-only) or Acceptor (non-pure, can mutate) functions.

OPTIONAL WHENPRESENT

Process the contained value only if present:

  optional.whenPresent(myConsumer)

The callback is never invoked if the Optional is empty. Safe by design.

RESULT WHENOK AND WHENERROR

Process ok or error values independently:

  result.whenOk(okConsumer)
  result.whenError(errorConsumer)

Each callback fires only if its corresponding value is present.

CONSUMER VS ACCEPTOR

Consumer is pure (read-only, no side effects). Acceptor can perform I/O or mutation:

  optional.whenPresent(pureProcessor)      Consumer
  result.whenOk(sideEffectProcessor)       Acceptor (when non-pure)

WHEN TO USE CALLBACKS VS GUARDS

Callbacks are cleaner when you just need to process the value. Guards are better when you need the value for further computation.

See Q54 for Consumer and Acceptor function types. See Q84 for Optional operations. See Q87 for Result operations.

See Q52 for dynamic functions. See Q51 for abstract functions.

Example

defines module qa.safeaccess.callbacks

  defines function

    logString() as pure
      -> content as String
      require content?

    logInteger() as pure
      -> code as Integer
      require code?

  defines program
    CallbacksDemo()
      stdout <- Stdout()

      // === OPTIONAL WHENPRESENT ===

      filled <- Optional("Hello")
      filled.whenPresent(logString)
      stdout.println("whenPresent on filled Optional: callback fired")

      emptyOpt <- Optional() of String
      emptyOpt.whenPresent(logString)
      stdout.println("whenPresent on empty Optional: callback NOT fired")

      // === RESULT WHENOK ===

      okResult <- Result("Success", Integer())
      okResult.whenOk(logString)
      stdout.println("whenOk on ok Result: callback fired")

      errResult <- Result(String(), 42)
      errResult.whenOk(logString)
      stdout.println("whenOk on error Result: callback NOT fired")

      // === RESULT WHENERROR ===

      errResult.whenError(logInteger)
      stdout.println("whenError on error Result: callback fired")

      okResult.whenError(logInteger)
      stdout.println("whenError on ok Result: callback NOT fired")

      // === BOTH CALLBACKS ON DUAL-VALUE RESULT ===

      bothResult <- Result("Partial", -1)
      bothResult.whenOk(logString)
      bothResult.whenError(logInteger)
      stdout.println("Both callbacks on dual-value Result")

      // === EMPTY RESULT — NEITHER FIRES ===

      emptyResult <- Result() of (String, Integer)
      emptyResult.whenOk(logString)
      emptyResult.whenError(logInteger)
      stdout.println("Empty Result: neither callback fires")

Common mistakes

E50060 — Optional does not have an ifPresent() method. The correct method name is whenPresent(). Calling a non-existent method triggers E50060 — method not resolved. See ek9 -h E50060 for details.

Incorrect:

filled.ifPresent(logString)

Correct:

filled.whenPresent(logString)
Other ways to ask this
  • How do whenPresent and whenOk work in EK9?
  • How do I use callbacks with Optional and Result in EK9?
  • What is the reactive pattern for Optional and Result in EK9?

Coming from another language?

Java: Optional.ifPresent(consumer), no Result type. Python: no callbacks on Optional (no Optional type). Rust: Option.map(), Result.map()/map_err() for transformation, no direct callback. Go: no Optional/Result, manual if checks. Kotlin: let/also/run scope functions, Result.onSuccess()/onFailure(). EK9: whenPresent for Optional, whenOk/whenError for Result, Consumer (pure) vs Acceptor (non-pure).

Keywords: null-safe, acceptor, safe, ok, reactive, access, callback, process, whenPresent, absent, error, whenOk, result, consumer, guard, optional, whenError