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