How do I safely unwrap an Optional value?

← Safe Value Access · Ref: Q163

EK9 Optionals require a compiler-enforced guard before accessing the contained value. There is no .unwrap() that could crash at runtime. Multiple patterns exist for safe unwrapping.

GUARD WITH IF

Declare and check in one expression:

  if val <- getOptional()
    extracted <- val.get()

The variable only exists inside the if block. If unset, the block is skipped entirely.

EXPLICIT ? CHECK

Check with ? then access inside the guarded block:

  opt <- getOptional()
  if opt?
    extracted <- opt.get()

The compiler verifies ? was checked before allowing .get().

GETORDEFAULT

Extract the value with a fallback, no guard needed:

  name <- opt.getOrDefault("default")

Returns the contained value if set, otherwise returns the default.

TERNARY GUARD

Compact single-expression extraction:

  name <- opt? <- opt.get() else "default"

If set, evaluates to opt.get(); otherwise uses the default.

See Q47 for Optional basics. See Q84 for Optional operations. See Q85 for Optional in streams. See Q634 for guard-based safe access. See Q636 for chained guard access.

Example

defines module qa.safeaccess.optionalsafe

  defines function

    getFilledOptional()
      <- rtn <- Optional("Hello")

    getEmptyOptional()
      <- rtn <- Optional() of String

  defines program
    OptionalUnwrapDemo()
      stdout <- Stdout()

      // === GUARD WITH IF (DECLARATION FORM) ===

      if val <- getFilledOptional()
        extracted <- val.get()
        stdout.println(`Guard unwrap: ${extracted}`)

      // Empty Optional — guard block not entered
      if val <- getEmptyOptional()
        stdout.println("Should not print")

      // === EXPLICIT ? CHECK ===

      opt <- getFilledOptional()
      if opt?
        checked <- opt.get()
        stdout.println(`? check unwrap: ${checked}`)

      // === GETORDEFAULT ===

      filled <- getFilledOptional()
      fromFilled <- filled.getOrDefault("fallback")
      stdout.println(`GetOrDefault (filled): ${fromFilled}`)

      emptyOpt <- getEmptyOptional()
      fromEmpty <- emptyOpt.getOrDefault("fallback")
      stdout.println(`GetOrDefault (empty): ${fromEmpty}`)

      // === TERNARY GUARD ===

      opt2 <- getFilledOptional()
      ternaryFilled <- opt2? <- opt2.get() else "default"
      stdout.println(`Ternary (filled): ${ternaryFilled}`)

      opt3 <- getEmptyOptional()
      ternaryEmpty <- opt3? <- opt3.get() else "default"
      stdout.println(`Ternary (empty): ${ternaryEmpty}`)

Common mistakes

E08030 — Calling .get() on an Optional without first checking with ? triggers E08030 — has not been checked before access. The compiler enforces that you verify the Optional is set before extracting its value. See ek9 -h E08030 for details.

Incorrect:

ternaryFilled <- opt2.get()

Correct:

ternaryFilled <- opt2? <- opt2.get() else "default"

E50060 — Optional does not have a .get(String) method. Use getOrDefault() for safe extraction with a fallback. Calling a non-existent method triggers E50060 — method not resolved. See ek9 -h E50060 for details.

Incorrect:

filled.get("fallback")

Correct:

filled.getOrDefault("fallback")
Other ways to ask this
  • How do I get the value from an Optional in EK9?
  • What is the safe way to access an Optional in EK9?
  • How do I handle empty Optionals in EK9?

Coming from another language?

Java: Optional.get() throws NoSuchElementException if empty, Optional.orElse() for defaults. Python: no Optional type, None checks are manual. Rust: Option.unwrap() panics if None, unwrap_or() for defaults. Go: no Optional, manual nil checks. Kotlin: nullable T? with !! force-unwrap (throws NPE), ?: elvis for defaults. EK9: compiler-enforced guards, .get() requires ? check, getOrDefault() always safe, no crash paths.

Keywords: absent, access, isSet, getOrDefault, unwrap, null, get, default, optional, null-safe, guard, safe, check, isset