Why does AI generate null checks instead of guards in EK9?
← What AI Gets Wrong About EK9 · Ref: Q277
AI models generate 'if result != null', 'if result is not None', or 'if result == null return' in EK9. There is no null in EK9. The keyword does not exist. EK9 uses a tri-state model (absent, unset, set) with guards.
THE AI MISTAKE
AI imports null-checking patterns from Java (null), Python (None), JavaScript (null/undefined), or Go (nil). None of these concepts exist in EK9.
THE EK9 TRI-STATE MODEL
Every EK9 object exists in one of three states:
Absent: does not exist (missing Dict key, empty Optional) Present but unset: exists but has no meaningful value (String() creates unset String) Present and set: exists with a valid value (String("hello") is set)
THE ? OPERATOR
Check whether an object is set with the ? operator:
if name? stdout.println(name)
This replaces null checks. The ? operator returns Boolean.
GUARDS
Guards combine assignment with the ? check in a single step:
if record <- findRecord(key) stdout.println(`Found: ${record}`)
The guard 'if record <- findRecord(key)' declares 'record', assigns the result, and checks if it is set. If unset, the block is skipped entirely. No null check needed.
GUARDS IN OTHER CONTROL FLOW
An if, switch or try guard stands on its own. A while or for guard must be followed by 'then' and the loop's own control, and the guard runs once, before the loop:
while value <- getPort() then count < limit process(value) for cfg <- getPort() then item in items process(cfg, item) switch port <- getPort() case > highPort useHighPort(port)
A bare 'while value <- getPort()' or 'for entry <- nextEntry()' with no 'then' is a syntax error. A switch case compares against the guarded value itself ('case > highPort'), not one of its members ('case .port > 8000' does not parse).
ASSIGN-IF-UNSET FOR DEFAULTS
The ':=?' operator assigns only if the target is unset:
host <- String() host :=? "localhost"
This replaces the null coalescing pattern (Java's 'x != null ? x : default').
See Q29 for tri-state semantics. See Q74 for guards. See Q85 for guards in all control flow. See Q243 for coalescing operators. See Q281 for verifying AI code.
Example
defines module qa.ai.mistakes.nullchecks defines function findByName() as pure -> searchName as String <- found as String: String() if searchName == "Alice" found: "Alice: Engineer" getPort() as pure <- port as Integer: 8080 defines program NullCheckDemo() stdout <- Stdout() // === GUARD REPLACES NULL CHECK === // AI would write: result = findByName("Alice"); if (result != null) print(result) // EK9 way: guard knownName <- "Alice" if record <- findByName(knownName) stdout.println(`Found: ${record}`) if record <- findByName("Unknown") stdout.println(record) else stdout.println("Not found, guard skipped the block") // === ? OPERATOR REPLACES NULL CHECK === // AI would write: if (name != null) print(name) // EK9 way: ? operator name <- String() if name? stdout.println(name) else stdout.println("Name is unset (not null, just unset)") name: findByName(knownName) if name? stdout.println(`Name is set: ${name}`) // === ASSIGN-IF-UNSET REPLACES NULL COALESCING === // AI would write: host = host != null ? host : "localhost" // EK9 way: :=? assign-if-unset host <- String() host :=? "localhost" stdout.println(`Host: ${host}`) host :=? "other" stdout.println(`Still: ${host}`) // === GUARD WITH FUNCTION RETURN === if port <- getPort() stdout.println(`Port: ${port}`)
Common mistakes
E01073 — EK9 has no null keyword. Use declaration guards with '<-' to check if a value is set and assign it in one step. See ek9 -h E01073 for details.
Incorrect:
if findByName(knownName) != null
Correct:
if record <- findByName(knownName)
E01073 — EK9 uses the ? operator to check if an object is set, not null comparisons. The ? operator returns Boolean. See ek9 -h E01073 for details.
Incorrect:
if name != null stdout.println(name)
Correct:
if name? stdout.println(name)
E01073 — EK9 uses the assign-if-unset operator :=? which only assigns if the variable is currently unset. This replaces null coalescing patterns. See ek9 -h E01073 for details.
Incorrect:
host = host != null <- host : "localhost"
Correct:
host :=? "localhost"
Other ways to ask this
- Why does AI use 'if x != null' in EK9?
- How do I replace AI-generated null checks in EK9?
- What does EK9 use instead of null?
Coming from another language?
Java: null checks everywhere, NullPointerException is most common exception, Optional since Java 8. Python: 'if x is not None', AttributeError from None. JavaScript: null AND undefined, loose equality traps. Kotlin: nullable types String?, safe call ?. operator, Elvis ?:. Rust: Option<T> with match/if-let. Go: nil checks. EK9: NO null, tri-state model (absent/unset/set), ? operator checks isSet, guards combine check and assignment, :=? assign-if-unset for defaults.
Keywords: safe, mistake, isset, null-safe, hallucination, migrate, check, nil, unset, common-error, pitfall, wrong, guard, tristate, none, set, absent, ai, null