Do the ?? and ?: operators check for null in EK9, and what is the difference between them?
← Operators and Expressions · Ref: Q1059
EK9 has no null. Its tri-state model has THREE states - ABSENT (no object at all), present but UNSET (an object with no meaningful value, e.g. String()), and SET - and the two coalescing operators test DIFFERENT things.
?? NULL COALESCING - tests ABSENCE only:
result <- name ?? "default"
Returns name whenever an object is present, EVEN IF IT IS UNSET. Only an ABSENT left operand gives "default". A present-but-unset value passes straight through, so the result is still unset.
?: COALESCE (ELVIS) - tests SET-NESS (absent or unset):
result <- name ?: "default"
Returns name only if it is SET. If name is unset (or absent) the result is "default".
THE DIFFERENCE IN ONE EXAMPLE
name <- String() present, but UNSET a <- name ?? "stranger" a is UNSET (?? saw an object, so it returned it) b <- name ?: "stranger" b is "stranger"
RULE OF THUMB
To give an UNSET value a default, use ?: (or :=? to assign in place). Values you declare and assign in EK9 are present, so ?? almost never does what a default needs - it is for a left operand that may not exist at all.
The distinction from other languages:
Java: name != null ? name : "default" checks null Kotlin: name ?: "default" checks null EK9: name ?? "default" checks absence only EK9: name ?: "default" checks isSet
In EK9, a variable can exist but be UNSET. This is not the same as null. An unset String is a String object that has no meaningful value yet. It still exists — you can call ? on it to check.
name <- String() // name exists, but is UNSET stdout.println(`${name?}`) // false (unset) name := "Alice" // now SET stdout.println(`${name?}`) // true
If BOTH sides of ?: are unset, the result is unset.
See Q243 for all coalescing operators. See Q898 for ? is not ternary. See Q883 for tri-state semantics.
Example
defines module qa.operators.coalescingisset defines program CoalescingDemo() stdout <- Stdout() //An UNSET String: the object is present, it just has no value name <- String() stdout.println(`name is set: ${name?}`) //?? only tests for ABSENCE - name is present, so the unset value passes straight through passedThrough <- name ?? "stranger" stdout.println(`?? result is set: ${passedThrough?}`) //?: tests SET-NESS - name is unset, so the default is used defaulted <- name ?: "stranger" stdout.println(`Hello ${defaulted}`) //Set the name name := "Alice" stdout.println(`name is set: ${name?}`) //Now name is set, so both operators return it viaNullCoalescing <- name ?? "guest" stdout.println(`Hello ${viaNullCoalescing}`) viaElvis <- name ?: "visitor" stdout.println(`Hello ${viaElvis}`)
Common mistakes
E01073 — 'null' does not exist in EK9 — a variable is either set or unset (tri-state), so assign a real value rather than 'null'. See ek9 -h E01073 for details.
Incorrect:
name := null
Correct:
name := "Alice"
Other ways to ask this
- Is ?? a null coalescing operator in EK9?
- How do ?? and ?: work — are they null checks?
- What do the coalescing operators actually check in EK9?
- Why does name ?? "default" not give me the default when name is unset?
- Should I use ?? or ?: to default an unset value in EK9?
Coming from another language?
Java: ternary with null check. Kotlin: ?: (elvis) checks null. JavaScript/C#/Swift: ?? (nullish coalescing) checks null. EK9: ?? checks only that an object is PRESENT (an unset value passes through); ?: checks that the value is SET - use ?: to default an unset value. EK9 has no null.
Keywords: default, coalescing, operator, null, isset, absent, elvis, unset