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