How do I associate data or behaviour with enum values in EK9?

← Enumerations · Ref: Q225

EK9 enumerations are pure value types with no methods or fields. Associate data using Dict with enum keys. Associate behaviour using functions that take the enum as a parameter.

DICT FOR ASSOCIATED DATA

Use a Dict with enum keys to map values to data:

  labels <- {Severity.Low: "Low priority", Severity.High: "Handle now"}

This separates the enumeration from its associated data, keeping both independently testable.

FUNCTIONS FOR BEHAVIOUR

Write functions that take the enum as a parameter:

  severityLabel() as pure
    -> s as Severity
    <- label as String: switch s ...

Behaviour lives in functions, not on the enum. Each function is independently testable.

SWITCH EXPRESSIONS FOR MAPPING

Use switch as an expression to map enum values to results:

  label <- switch severity
    <- rtn as String: String()
    case Severity.Low
      rtn: "monitor"
    case Severity.High
      rtn: "escalate"

Exhaustive switch guarantees all values are handled.

WHY COMPOSITION

Alan Perlis: 100 functions on one data structure beat 10 functions on 10 structures. Keeping enums pure means they stay serialisable, comparable, iterable, and JSON-convertible. Behaviour in functions means testable, composable, and replaceable logic.

See Q100 for enum vs Java comparison. See Q109 for composition pattern. See Q212 for composition over inheritance. See Q219 for auto-generated operators. See Q746 for migrating Swift enum associated values to EK9 composition.

Example

defines module qa.enums.composition

  defines type

    Severity
      Info
      Warning
      Error
      Critical

  defines function

    severityLabel() as pure
      -> s as Severity
      <- label as String: switch s
        <- rtn as String: String()
        case Severity.Info
          rtn: "Information - log and continue"
        case Severity.Warning
          rtn: "Warning - investigate soon"
        case Severity.Error
          rtn: "Error - fix required"
        case Severity.Critical
          rtn: "Critical - immediate action needed"
        default
          rtn: "No severity set"

    isActionRequired() as pure
      -> s as Severity
      <- required <- Boolean()
      required: s >= Severity.Error

  defines program

    EnumCompositionDemo()
      stdout <- Stdout()

      // === FUNCTIONS FOR BEHAVIOUR ===

      for s in Severity
        stdout.println(severityLabel(s))

      // === FUNCTIONS WITH COMPARISON ===

      for s in Severity
        stdout.println(`${s} requires action: ${isActionRequired(s)}`)

      // === DICT FOR ASSOCIATED DATA ===

      icons <- {Severity.Info: "i", Severity.Warning: "!", Severity.Error: "X", Severity.Critical: "!!!"}

      for s in Severity
        icon <- icons.getOrDefault(s, "?")
        stdout.println(`${s}: ${icon}`)

      // === SWITCH EXPRESSION FOR MAPPING ===

      testSeverity <- Severity.Warning
      category <- switch testSeverity
        <- rtn as String: String()
        case Severity.Info, Severity.Warning
          rtn: "monitor"
        case Severity.Error, Severity.Critical
          rtn: "escalate"
        default
          rtn: "unknown"
      stdout.println(`${testSeverity} category: ${category}`)

Common mistakes

E50060 — EK9 Dict does not have a 'get()' method. Use 'getOrDefault(key, default)' to retrieve values from a Dict, or use 'contains' to check for key existence first. See ek9 -h E50060 for details.

Incorrect:

icons.get(s)

Correct:

icons.getOrDefault(s, "?")
Other ways to ask this
  • Can EK9 enums have methods or fields?
  • How do I add behaviour to an EK9 enumeration?
  • What is the EK9 pattern for enum-associated data?

Coming from another language?

Java: puts methods, fields, and constructors ON the enum (Planet with mass/radius/surfaceGravity()), creates tight coupling, harder to serialise. Python: adds methods to Enum class, can override __str__, mixes concerns. Rust: associates data per variant with enum fields, pattern matching extracts data. Go: methods on integer type, but enum is just integers. Kotlin: enum class with properties and abstract method implementations per entry. Swift: enum cases carry associated values per variant (case circle(radius: Double)), extracted via pattern matching, powerful but couples identity to data. C#: extension methods on enum type, or attributes for metadata. EK9: enums are pure values, use Dict for data association and functions for behaviour, separation of concerns.

Keywords: function, composition, behaviour, separate, dict, immutable, associate, pure, pattern, swift, migrate, design, enum, enumeration, side-effect