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