How do coalescing operators handle unset values in EK9?
← Operators and Expressions · Ref: Q243
EK9 provides coalescing operators that safely handle unset values. These are expression-level operators (not implementable on your types) that the compiler handles directly.
NULL COALESCING (??)
The ?? operator checks if the left operand exists in memory. If the left is invalid (absent), it returns the right.
result <- possiblyAbsent ?? fallback
This is a memory-level check, not an isSet check.
ELVIS OPERATOR (?:)
The ?: operator checks if the left is both present AND set (via isSet). If the left is absent or unset, it returns the right.
result <- possiblyUnset ?: fallback
This is more thorough than ?? because it also checks the isSet state.
COALESCING COMPARISONS
These operators compare two values, handling the case where one or both might be unset:
<? (lesser coalescing): returns the lesser value, or whichever is valid <=? (lesser-or-equal coalescing): returns the lesser-or-equal value >? (greater coalescing): returns the greater value, or whichever is valid >=? (greater-or-equal coalescing): returns the greater-or-equal value
Priority rules: If left is invalid, return right. If right is invalid, return left. If both valid, compare and return the winner.
minimum <- a <? b maximum <- a >? b
NOTE: :=? is NOT a coalescing operator. It is an ASSIGNMENT operator that assigns only if the target is unset. See Q1152 for :=? details. See Q1226 for <? vs :=? contrast.
REPLACING VERBOSE IF/ELSE
Without coalescing:
if a? if b? if a < b result: a else result: b else result: a else result: b
With coalescing:
result <- a <? b
One line replaces a nested if/else tree.
See Q75 for guard switch. See Q161 for Dict safe access. See Q168 for fallback values. See Q239 for comparison operators. See Q238 for the complete operator set.
Example
defines module qa.operators.coalescing defines program CoalescingDemo() stdout <- Stdout() // === ELVIS OPERATOR ?: === name <- String() displayName <- name ?: "Anonymous" stdout.println(`Display name: ${displayName}`) // === COALESCING COMPARISONS === a <- 10 b <- 20 minimum <- a <? b maximum <- a >? b stdout.println(`Minimum: ${minimum}`) stdout.println(`Maximum: ${maximum}`) // === CHAINED COALESCING === primary <- String() secondary <- String() fallback <- "last-resort" chosen <- primary ?: secondary ?: fallback stdout.println(`Chosen: ${chosen}`)
Common mistakes
E04020 — Coalescing operators require both operands to be the same type. Mixing String and Integer triggers E04020 — incompatible types in coalescing expression. See ek9 -h E04020 for details.
Incorrect:
displayName <- name ?: 42
Correct:
displayName <- name ?: "Anonymous"
Other ways to ask this
- What is the null coalescing operator in EK9?
- How does the Elvis operator work in EK9?
- How do I pick the lesser or greater of two possibly unset values?
Coming from another language?
Java: ternary x != null ? x : default, no null coalescing operator, Optional.orElse(). Python: x if x is not None else default, or shorthand x or default (falsy gotcha). Rust: unwrap_or(), unwrap_or_else(), no null concept. Go: if x == nil then default, no ternary or coalescing. Kotlin: ?: elvis operator for null, no coalescing comparisons. Swift: ?? nil coalescing (same operator as EK9!), no equivalent of ?: isSet check or <? >? coalescing comparisons. JavaScript: ?? null coalescing (ES2020), || logical or fallback. EK9: ?? (memory check), ?: (isSet check), <? <=? >? >=? (coalescing comparisons), :=? (conditional assignment).
Keywords: expression, coalescing, safe, operator, unset, null, elvis, assignment, default, greater, absent, lesser, isset, conditional, migrate, swift, fallback