What happens when an EK9 enum is unset?

← Enumerations · Ref: Q222

EK9 enumerations follow tri-state semantics: a variable can be set to a specific member, or it can be unset. There is no null, no exception, and no undefined behaviour.

THREE WAYS TO GET UNSET

Priority() creates an unset enum. Priority("Invalid") returns unset when the string does not match any member. Incrementing past the last member or decrementing before the first member also produces unset.

UNSET PROPAGATION

Comparison with an unset enum returns an unset Boolean. String conversion of an unset enum returns an unset String. JSON conversion of an unset enum returns an unset JSON. The unset state flows through operations naturally.

FIRST AND LAST ON UNSET

The #< (first) and #> (last) operators are type-level operations. They always return the first or last declared member, even when applied to an unset variable. This makes them safe anchors for restarting navigation.

CHECKING WITH ?

Use the ? operator to check if an enum is set. Guard expressions work naturally: 'if checked <- getStatus()' only enters the block when the result is set.

See Q29 for unset variables. See Q219 for all auto-generated operators. See Q220 for navigation. See Q221 for string comparison.

Example

defines module qa.enums.unset

  defines type

    Priority
      Low
      Medium
      High
      Critical

  defines program

    EnumUnsetDemo()
      stdout <- Stdout()

      // === THREE WAYS TO GET UNSET ===

      fromDefault <- Priority()
      stdout.println(`Default constructor isSet: ${fromDefault?}`)

      fromBadString <- Priority("Urgent")
      stdout.println(`Bad string isSet: ${fromBadString?}`)

      pastLast <- Priority.Critical
      pastLast++
      stdout.println(`Past last isSet: ${pastLast?}`)

      // === UNSET PROPAGATION ===

      unsetPriority <- Priority()
      setPriority <- Priority.High

      eqResult <- unsetPriority == setPriority
      stdout.println(`Unset == Set isSet: ${eqResult?}`)

      cmpResult <- unsetPriority <=> setPriority
      stdout.println(`Unset <=> Set isSet: ${cmpResult?}`)

      unsetStr <- $unsetPriority
      stdout.println(`Unset string isSet: ${unsetStr?}`)

      unsetHash <- #?unsetPriority
      stdout.println(`Unset hashcode isSet: ${unsetHash?}`)

      // === FIRST AND LAST STILL WORK ON UNSET ===

      firstFromUnset <- #< unsetPriority
      stdout.println(`First from unset: ${firstFromUnset}`)

      lastFromUnset <- #> unsetPriority
      stdout.println(`Last from unset: ${lastFromUnset}`)

      // === CHECKING WITH ? ===

      if setPriority?
        stdout.println(`Set priority: ${setPriority}`)

      if unsetPriority?
        stdout.println("This will not print")

Common mistakes

E50060 — EK9 enumerations do not have an 'equals()' method. Use the == operator for equality comparison. The == operator handles unset values safely by producing an unset Boolean result. See ek9 -h E50060 for details.

Incorrect:

eqResult <- unsetPriority.equals(setPriority)

Correct:

eqResult <- unsetPriority == setPriority
Other ways to ask this
  • How does EK9 handle null enum values?
  • What is the tri-state behaviour of EK9 enumerations?
  • How does unset propagation work with EK9 enums?

Coming from another language?

Java: throws NullPointerException on null enum reference, IllegalArgumentException on bad valueOf(). Python: raises ValueError on invalid Enum construction, None requires explicit null checks. Rust: Option<EnumType> requires explicit unwrapping with match or if-let, no propagation. Go: zero value is integer 0 which may silently represent a valid enum constant. C#: nullable enum exists but no propagation semantics, requires explicit null checks. Kotlin: nullable enum with ?. operator but no systematic unset propagation. EK9: systematic tri-state with natural propagation through all operators, check with ? when ready.

Keywords: safe, enumeration, absent, unset, enum, propagation, exception, tri-state, null, check, set