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 guard expressions.

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.

GUARD EXPRESSIONS

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 WORK EVERYWHERE

The same guard syntax works identically in all control flow:

  while connection <- getActive()
    transferData(connection)
  for entry <- nextEntry()
    process(entry)
  switch config <- loadConfig()
    case .port > 8000
      useHighPort(config)

GUARDED ASSIGNMENT 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 guard expressions. 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 EXPRESSION REPLACES NULL CHECK ===
      // AI would write: result = findByName("Alice"); if (result != null) print(result)
      // EK9 way: guard expression

      if record <- findByName("Alice")
        stdout.println(`Found: ${record}`)

      if record <- findByName("Unknown")
        stdout.println("Found someone")
      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("Has a name")
      else
        stdout.println("Name is unset (not null, just unset)")

      name: findByName("Steve")
      if name?
        stdout.println(`Name is set: ${name}`)

      // === GUARDED ASSIGNMENT REPLACES NULL COALESCING ===
      // AI would write: host = host != null ? host : "localhost"
      // EK9 way: :=? guarded assignment

      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 guard expressions with '<-' to check if a value is set and assign it in one step. See ek9 -h E01073 for details.

Incorrect:

if findByName("Alice") != null

Correct:

if record <- findByName("Alice")

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

Correct:

if name?

E01073 — EK9 uses the guarded assignment 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, guard expressions combine check and assignment, :=? guarded assignment for defaults.

Keywords: pitfall, common-error, unset, nil, check, migrate, hallucination, null-safe, isset, mistake, safe, null, ai, absent, set, none, tristate, guard, wrong