How do I migrate Swift enum associated values to EK9?

← Enumerations · Ref: Q746

Swift enums can carry data per variant (associated values). EK9 enums are pure value types with no fields or methods. The EK9 equivalent uses enum + Dict for data, or enum + class hierarchy for complex variants.

SWIFT PATTERN: ASSOCIATED VALUES

Swift lets each enum case carry different data:

  enum Shape {
    case circle(radius: Double)
    case rectangle(width: Double, height: Double)
  }

Swift extracts data via pattern matching in switch.

EK9 PATTERN 1: ENUM + DICT FOR SIMPLE DATA

When each variant maps to a single value, use a Dict:

  defines type
    Colour: Red, Green, Blue
  labels <- {Colour.Red: "#FF0000", Colour.Green: "#00FF00", Colour.Blue: "#0000FF"}

This separates identity (enum) from data (Dict), keeping both independently testable and serialisable.

EK9 PATTERN 2: TRAIT HIERARCHY FOR COMPLEX VARIANTS

When variants carry different shaped data (like Swift's Shape example), use a trait with classes:

  defines trait
    Shape
      area() as pure abstract
        <- rtn as Float?
  defines class
    Circle with trait of Shape
      radius <- 0.0
      override area() as pure
        <- rtn as Float: 3.14159 * radius * radius
    Rectangle with trait of Shape
      ...

Each class carries its own data. The trait defines the shared contract.

WHY COMPOSITION IS SUPERIOR

Swift's associated values tightly couple identity and data. EK9's separation gives:
- Serialisability: enums convert to JSON automatically, associated values need custom Codable
- Testability: functions and Dict entries are independently testable
- Extensibility: add new data mappings without modifying the enum
- Quality: the compiler enforces metrics on each function independently

See Q100 for enum vs Java comparison. See Q219 for auto-generated operators. See Q225 for the composition pattern in detail. See Q106 for traits. See Q109 for composition over inheritance.

Example

defines module qa.enums.swiftmigration

  defines type

    Colour
      Red
      Green
      Blue

    Shape
      Circle
      Rectangle
      Triangle

  defines trait

    <?-
      Shared contract for shapes with area calculation.
      This replaces Swift's enum with associated values.
    -?>
    Measurable
      area() as pure abstract
        <- rtn as Float?

  defines class

    CircleShape with trait of Measurable
      radius <- 0.0

      CircleShape()
        -> radius as Float
        this.radius: radius

      override area() as pure
        <- rtn as Float: 3.14159 * radius * radius

      default operator

    RectangleShape with trait of Measurable
      width <- 0.0
      height <- 0.0

      RectangleShape()
        ->
          width as Float
          height as Float
        this.width: width
        this.height: height

      override area() as pure
        <- rtn as Float: width * height

      default operator

  defines function

    <?-
      Maps shape type to a description string.
      This replaces Swift's associated value extraction.
    -?>
    describeShape() as pure
      -> shapeType as Shape
      <- description as String: switch shapeType
        <- rtn as String: String()
        case Shape.Circle
          rtn: "A round shape"
        case Shape.Rectangle
          rtn: "A four-sided shape"
        case Shape.Triangle
          rtn: "A three-sided shape"
        default
          rtn: "Unknown shape"

  defines program

    SwiftEnumMigrationDemo()
      stdout <- Stdout()

      // === PATTERN 1: ENUM + DICT FOR SIMPLE DATA ===

      hexCodes <- {Colour.Red: "#FF0000", Colour.Green: "#00FF00", Colour.Blue: "#0000FF"}

      for colour in Colour
        code <- hexCodes.getOrDefault(colour, "unknown")
        stdout.println(`${colour}: ${code}`)

      // === ENUM + FUNCTION FOR BEHAVIOUR ===

      for shape in Shape
        stdout.println(describeShape(shape))

      // === PATTERN 2: TRAIT HIERARCHY FOR COMPLEX VARIANTS ===

      circle <- CircleShape(5.0)
      rect <- RectangleShape(4.0, 6.0)

      stdout.println(`Circle area: ${circle.area()}`)
      stdout.println(`Rectangle area: ${rect.area()}`)

Common mistakes

E50060 — String has no toUpperCase() method in EK9. Use upperCase() instead. See ek9 -h E50060 for details.

Incorrect:

stdout.println(describeShape(shape).toUpperCase())

Correct:

stdout.println(describeShape(shape))

E50060 — String has no toUpperCase() method in EK9. Use upperCase() instead. See ek9 -h E50060 for details.

Incorrect:

code <- hexCodes.getOrDefault(colour, "unknown").toUpperCase()

Correct:

code <- hexCodes.getOrDefault(colour, "unknown")
Other ways to ask this
  • What is the EK9 equivalent of Swift enum with associated values?
  • How do I model Swift's enum cases with data in EK9?
  • How do I replace Swift associated values with EK9 composition?

Coming from another language?

Swift: enum cases carry associated values (case circle(radius: Double)), extracted via pattern matching (case .circle(let r)), powerful but couples data to identity. Rust: enum variants with fields, extracted via match arms. Java: enum constants with fields and constructors. Kotlin: sealed class with data class variants. EK9: enums are pure values, use Dict for simple data mapping and trait + class hierarchy for complex variant data, separation of concerns.

Keywords: swift, values, dict, associated, composition, data, variant, enum, pattern, enumeration, trait, migrate