How do I run a callback over a collection that is held inside an Optional or Result?

← Generics · Ref: Q1372

A built-in generic container (Optional, Result) can be parameterised over another generic type - Optional of (List of String), Result of (List of String, Integer) - and its callback methods hand the FULLY-TYPED value to your function, exactly as the return methods (get/ok) do. You write the callback the same way you would over a simple element.

OPTIONAL OF A LIST - whenPresent

  items <- List() of String
  items += "alpha"
  opt <- Optional(items)
  printer <- (stdout) is Acceptor of List of String as function
    stdout.println(`present: ${$t}`)
  opt.whenPresent(printer)          // printer receives a List of String

RESULT OF A LIST - whenOk / whenError

  listResult <- Result(items, 0)
  onOk <- (stdout) is Acceptor of List of String as function
    stdout.println(`ok: ${$t}`)
  listResult.whenOk(onOk)

The callback parameter is the SAME type you parameterised the container with. Whether the element is a simple type (String) or itself a generic (List of String, Dict of (String, Integer)), the value you receive is the typed wrapper - never a raw internal delegate.

GUARD BEFORE get()/ok()
The callback forms (whenPresent/whenOk/whenError) fire only in the right state, so they need no guard. The value-returning forms do - guard first:

  if opt?
    got <- opt.get()

Use the callback form when you just want to act on the value, the get form when you need it in the surrounding scope.

See Q47 for Optional. See Q48 for Result. See Q707 for Consumer/Acceptor. See Q198 for the built-in generics.

Example

defines module qa.genericsdeep.collectioncallback

  defines program

    CollectionCallbackDemo()
      stdout <- Stdout()

      items <- List() of String
      items += "alpha"
      items += "beta"

      // Optional of (List of String) - whenPresent hands the typed List to the callback
      opt <- Optional(items)
      printer <- (stdout) is Acceptor of List of String as function
        stdout.println(`present: ${$t}`)
      opt.whenPresent(printer)

      // Result of (List of String, Integer) - whenOk hands the typed List to the callback
      listResult <- Result(items, 0)
      onOk <- (stdout) is Acceptor of List of String as function
        stdout.println(`ok: ${$t}`)
      listResult.whenOk(onOk)

      // Value-returning form needs a guard first
      if opt?
        got <- opt.get()
        stdout.println(`got: ${$got}`)

Common mistakes

E08030 — Optional.get() must be guarded by a set-check first (E08030). The whenPresent callback form needs no guard because it only fires when a value is present. See ek9 -h E08030 for details.

Incorrect:

got <- opt.get()

Correct:

if opt?
        got <- opt.get()
Other ways to ask this
  • whenPresent over an Optional of a List
  • whenOk / whenError with a List value in a Result
  • Pass a Consumer to a container whose element is itself a generic type
  • Callback receives a List of String from an Optional

Coming from another language?

Java: optional.ifPresent(list -> ...) where list is List<String>; the type is preserved. EK9: opt.whenPresent(acceptor) where the Acceptor is 'of List of String' - identical mental model, and the compiler guarantees the callback receives the typed value even when the element is itself a parameterised type.

Keywords: container, Consumer, whenPresent, generic, whenError, parameterised, Result, callback, Optional, whenOk, Acceptor, collection, List