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 :=? assign-if-unset, getOrDefault(), and the ternary guard pattern.

ASSIGN-IF-UNSET (:=?)

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:

  ages <- {"Alice": 30}
  numbers <- [10, 20]
  maybe <- Optional() of Integer
  fromDict <- ages.getOrDefault("Bob", 0)
  fromList <- numbers.getOrDefault(5, 0)
  fromOptional <- maybe.getOrDefault(0)

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 assign-if-unset. 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()

      // === ASSIGN-IF-UNSET (:=?) ===

      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 — Assign-if-unset :=? 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 assign-if-unset 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 assign-if-unset. 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: :=? assign-if-unset for layered defaults, getOrDefault() on containers, ternary guard pattern.

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