What is the default value of an unset variable?
← Getting Started · Ref: Q29
EK9 has no null. Every object exists in one of three states: absent, present but unset, or present and set. The ? operator checks whether an object is set.
THE THREE STATES
Absent: object does not exist (Dict lookup miss, empty Optional). Present but unset: object exists, no meaningful value. String() is unset. ? returns false. Present and set: object has valid value. String("hello") is set. ? returns true.
CHECKING STATE WITH ?
name <- String() if name? stdout.println("Has a value")
Guarded assignment :=? only assigns if unset:
name :=? "default" assigns because unset name :=? "other" no-op because already set
GUARDS IN CONTROL FLOW
if record <- database.find(key) stdout.println(`Found: ${record}`)
Works identically in if, switch, for, while, and try.
TYPE-SPECIFIC BEHAVIOUR
Primitives (String, Integer, Float, Boolean, Character): unset when created with no argument. Collections (List, Dict): always set when created, even if empty. Containers (Optional, DictEntry): depends on contained value.
INVALID CONSTRUCTION CREATES UNSET
parsed <- Integer("not a number") if parsed? false — invalid input produces unset, no exception
BOOLEAN TRI-STATE
Boolean() is unset (not yet known), distinct from Boolean(false).
JSON MAPPING
Absent: field omitted. Unset: field as null. Set: field has value.
Critical for REST PATCH distinguishing 'not provided' from 'clear value' from 'update'.
WHY NOT NULL?
Null is 'the billion-dollar mistake'. EK9's tri-state replaces it: objects always exist, ? checks for meaningful value, guards prevent processing unset values, compiler enforces handling.
See Q22 for :=? guarded assignment. See Q26 for unified object model. See Q30 for Boolean tri-state. See Q47 for Optional. See Q48 for Result. See Q104 for uninitialised properties. See Q141 for constants. See Q188 for JSON. See Q222 for enum unset state. See Q243 for coalescing operators. See Q251 for unset variable errors. See Q256 for Void type. See Q261 for idiomatic patterns. See Q269 for input validation. See Q277 for AI null checks vs guards. See Q286 for guarded assignment accumulation.
Example
defines module qa.unset.variables defines program UnsetVariablesDemo() stdout <- Stdout() // Present but unset name <- String() stdout.println(`Name is set: ${name?}`) // Present and set greeting <- "Hello" stdout.println(`Greeting is set: ${greeting?}`) // Guarded assignment - only assigns if unset name :=? "default" stdout.println(`Name after guard: ${name}`) name :=? "other" stdout.println(`Name after second guard: ${name}`) // Guard in control flow items <- List() of String items += "first" // Collections are always set when created stdout.println(`Items is set: ${items?}`) emptyList <- List() of Integer stdout.println(`Empty list is set: ${emptyList?}`) // Unset integer count <- Integer() stdout.println(`Unset count: ${count?}`) count :=? 42 stdout.println(`Count after guard: ${count}`)
Other ways to ask this
- What happens when a variable has no value in EK9?
- How does the tri-state object model work in EK9?
- Does EK9 have null?
- How do I check if a variable is set in EK9?
Coming from another language?
Java: null/NullPointerException. Rust: Option<T>. Kotlin/Swift: nullable types. Python: None. JS: null AND undefined. EK9: tri-state (absent/unset/set), no null, ? operator, :=? guarded assignment, guards in control flow.
Keywords: tri-state, intro, migrate, unset, null, tristate, billion, start, default, mistake, absent, swift, first, beginner, value, undefined