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