How does EK9 prevent common enum bugs?

← Enumerations · Ref: Q226

EK9 prevents entire categories of enum bugs that plague other languages. These protections are automatic and cannot be bypassed.

EXHAUSTIVE SWITCH ENFORCEMENT

When you switch on an enum using direct constant references, the compiler verifies ALL values are covered. Adding a new value to the enum forces you to handle it everywhere. No silent missing-case bugs.

BOUNDARY TO UNSET

Incrementing past the last value or decrementing before the first produces unset, not an index error or silent wraparound. Check with ? when you need to know.

SAFE STRING CONSTRUCTION

Constructing from an invalid string returns unset, not an exception. No try-catch overhead. No risk of uncaught IllegalArgumentException crashing production.

IMMUTABLE CONSTANTS

Enum values are immutable constants. You can compare and copy them but never modify the enum definition at runtime. No reflection tricks, no monkey-patching.

NO INTEGER CASTING

EK9 enums are not integers. You cannot accidentally pass 42 where a Season is expected. The type system prevents it at compile time.

UNSET PROPAGATION

Operations on unset enums produce unset results. No NullPointerException. No undefined behaviour. The unset state flows safely through your program until you check it.

See Q73 for exhaustive switch details. See Q99 for basic enumerations. See Q219 for all auto-generated operators. See Q222 for unset semantics.

Example

defines module qa.enums.safety

  defines type

    Status
      Pending
      Active
      Paused
      Complete

  defines function

    describeStatus() as pure
      -> s as Status
      <- description <- String()

      //Exhaustive: compiler forces ALL values to be handled
      switch s
        case Status.Pending
          description: "waiting to start"
        case Status.Active
          description: "in progress"
        case Status.Paused
          description: "temporarily stopped"
        case Status.Complete
          description: "finished"
        default
          description: "no status set"

  defines program

    EnumSafetyDemo()
      stdout <- Stdout()

      // === EXHAUSTIVE SWITCH: all values must be covered ===

      for s in Status
        stdout.println(`${s}: ${describeStatus(s)}`)

      // === BOUNDARY TO UNSET: no index errors ===

      last <- Status.Complete
      last++
      stdout.println(`Past last isSet: ${last?}`)

      first <- Status.Pending
      first--
      stdout.println(`Before first isSet: ${first?}`)

      // === SAFE STRING CONSTRUCTION: no exceptions ===

      valid <- Status("Active")
      stdout.println(`Valid: ${valid}, isSet: ${valid?}`)

      invalid <- Status("Running")
      stdout.println(`Invalid isSet: ${invalid?}`)

      // === IMMUTABLE CONSTANTS: copy, do not modify ===

      original <- Status.Active
      copied <- Status(original)
      stdout.println(`Original: ${original}, Copy: ${copied}`)

      // === UNSET PROPAGATION: no null exceptions ===

      unsetStatus <- Status()
      result <- unsetStatus == Status.Active
      stdout.println(`Unset comparison isSet: ${result?}`)

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

Common mistakes

E50060 — EK9 enumerations do not have a 'valueOf()' method like Java enums. Use the String constructor 'Status("Active")' for safe string construction. Invalid strings return unset instead of throwing exceptions. See ek9 -h E50060 for details.

Incorrect:

valid <- Status.valueOf("Active")

Correct:

valid <- Status("Active")
Other ways to ask this
  • What enum bugs does EK9 eliminate at compile time?
  • Why are EK9 enums safer than in other languages?
  • How does EK9 protect against enum-related errors?

Coming from another language?

C/C++: enums are integers, any integer value accepted, no type safety, missing switch cases are optional warnings only. Java: null enum throws NullPointerException, valueOf throws IllegalArgumentException, exhaustive switch only since Java 21 with sealed types. Python: no exhaustive checking in match/case for enums, Enum construction can raise ValueError. Go: iota constants are integers with zero type safety, any int passes. Rust: exhaustive match is strong, but no boundary-to-unset or safe string construction. Kotlin: when-expression exhaustive only when used as expression, nullable enums require explicit handling. C#: integer-backed, Enum.IsDefined for runtime checking only. EK9: compile-time exhaustive switch, boundary-to-unset, safe string construction, no integer casting, unset propagation.

Keywords: exhaustive, bug, enumeration, compile, boundary, safety, prevent, switch, enum, protection, immutable