How do I provide a fallback value when something might be unset?

← Safe Value Access · Ref: Q168

EK9 provides several mechanisms for fallback values: the :=? guarded assignment, getOrDefault(), and the ternary guard pattern.

GUARDED ASSIGNMENT (:=?)

Only assigns if the target is currently unset:

  name <- String()
  name :=? "default"

If name was unset, it becomes "default". If already set, unchanged. This is ideal for layered defaults.

LAYERED DEFAULTS

Apply multiple fallback layers:

  setting <- String()
  setting :=? userPreference
  setting :=? systemDefault
  setting :=? "hardcoded"

Each :=? only applies if the variable is still unset.

GETORDEFAULT

Extract from containers with a fallback:

  dict.getOrDefault(key, default)
  list.getOrDefault(index, default)
  optional.getOrDefault(default)

TERNARY GUARD

Single-expression fallback:

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

GUARD IN IF

Conditional processing with fallback in else:

  if val <- getOptional()
    stdout.println(val.get())
  else
    stdout.println("No value available")

See Q22 for variable declaration. See Q29 for tri-state semantics. See Q79 for guarded assignment. See Q84 for Optional ternary guard.

See Q29 for unset variables. See Q166 for consistent safe pattern. See Q243 for coalescing operators.

Example

defines module qa.safeaccess.fallback

  defines program
    FallbackValuesDemo()
      stdout <- Stdout()

      // === GUARDED ASSIGNMENT (:=?) ===

      name <- String()
      stdout.println(`Before guard: isSet=${name?}`)

      name :=? "default"
      stdout.println(`After first guard: ${name}`)

      name :=? "other"
      stdout.println(`After second guard (unchanged): ${name}`)

      // === LAYERED DEFAULTS ===

      // Simulate layered configuration
      userPref <- String()
      systemDefault <- String()
      hardcoded <- "en-US"

      locale <- String()
      locale :=? userPref
      locale :=? systemDefault
      locale :=? hardcoded
      stdout.println(`Locale (from hardcoded): ${locale}`)

      // Now with user preference set
      locale2 <- String()
      userPref2 <- "fr-FR"
      locale2 :=? userPref2
      locale2 :=? "en-US"
      stdout.println(`Locale (from user): ${locale2}`)

      // === GETORDEFAULT ON CONTAINERS ===

      config <- {"debug": "true"}
      debugMode <- config.getOrDefault("debug", "false")
      verboseMode <- config.getOrDefault("verbose", "false")
      stdout.println(`Debug: ${debugMode}`)
      stdout.println(`Verbose: ${verboseMode}`)

      items <- [100, 200, 300]
      firstItem <- items.getOrDefault(0, -1)
      missingItem <- items.getOrDefault(99, -1)
      stdout.println(`First: ${firstItem}`)
      stdout.println(`Missing: ${missingItem}`)

      // === TERNARY GUARD ===

      opt <- Optional("Hello")
      greeting <- opt? <- opt.get() else "Hi"
      stdout.println(`Greeting: ${greeting}`)

      emptyOpt <- Optional() of String
      fallbackGreeting <- emptyOpt? <- emptyOpt.get() else "Hi"
      stdout.println(`Fallback greeting: ${fallbackGreeting}`)

Common mistakes

E50030 — Guarded assignment :=? requires the value type to match the variable type. Assigning an Integer to a String variable triggers E50030 — types are not compatible. Ensure the fallback value matches the declared type. See ek9 -h E50030 for details.

Incorrect:

name :=? 42

Correct:

name :=? "default"
Other ways to ask this
  • How do I set a default value for an unset variable in EK9?
  • What is the guarded assignment operator in EK9?
  • How do I chain fallback values in EK9?

Coming from another language?

Java: ternary operator (x != null ? x : default), Optional.orElse(), no guarded assignment. Python: x = x or default (falsy gotcha), x if x is not None else default. Rust: unwrap_or(), unwrap_or_else() for lazy defaults. Go: if x == nil { x = default }. Kotlin: ?: elvis operator for null defaults. Swift: ?? nil coalescing for optional defaults, if let with else for guarded fallback. JavaScript: ?? nullish coalescing, || logical or. EK9: :=? guarded assignment for layered defaults, getOrDefault() on containers, ternary guard pattern.

Keywords: layered, isset, getOrDefault, access, null-safe, priority, chain, safe, assignment, fallback, guarded, unset, swift, ternary, default