How does allow only work with dispatchers in EK9?

← Sealed Types and Traits · Ref: Q299

When a dispatcher's parameter type is a sealed trait (one with 'allow only'), the compiler requires handlers for all permitted types. This provides exhaustive matching, similar to Java's sealed switch or Kotlin's sealed when.

EXHAUSTIVE DISPATCH

A dispatcher on a sealed trait must have a handler for every type in the 'allow only' list:

  ShapeProcessor
    process() as dispatcher
      -> shape as Shape
    process()
      -> circle as Circle
    process()
      -> square as Square
    process()
      -> triangle as Triangle

COMPILE-TIME SAFETY

When you add a new type to the 'allow only' list, every dispatcher that operates on that trait will fail to compile until a handler is added. This guarantees all dispatch points are updated.

UNSEALED TRAITS

Dispatchers on unsealed traits (without 'allow only') do not require exhaustive handlers. The entry point acts as a catch-all fallback.

See Q298 for 'allow only' basics. See Q105 for dispatcher patterns. See Q618 for sealed exhaustive dispatch deep dive.

Example

defines module qa.sealedtraits.dispatchers

  defines trait

    Shape allow only Circle, Square, Triangle
      area() as abstract
        <- rtn as Float?

  defines class

    Circle with trait of Shape
      override area()
        <- rtn as Float: 3.14

    Square with trait of Shape
      override area()
        <- rtn as Float: 1.0

    Triangle with trait of Shape
      override area()
        <- rtn as Float: 0.5

    ShapeProcessor

      process() as dispatcher
        -> shape as Shape
        content <- "Unknown shape"
        require content?

      process()
        -> circle as Circle
        content <- "Circle"
        require content?

      process()
        -> square as Square
        content <- "Square"
        require content?

      process()
        -> triangle as Triangle
        content <- "Triangle"
        require content?

  defines function

    testDispatchers()
      processor <- ShapeProcessor()
      require processor?

Common mistakes

E05120 — Implementing a trait's abstract method requires the 'override' keyword. Omitting it causes E05120 because the compiler sees an inherited method being redefined without acknowledgement. See ek9 -h E05120 for details.

Incorrect:

Circle with trait of Shape
      area()
        <- rtn as Float: 3.14

Correct:

Circle with trait of Shape
      override area()
        <- rtn as Float: 3.14

E50001 — Renaming the variable means later references to 'processor' become unresolved, triggering E50001. Variable names must be consistent. See ek9 -h E50001 for details.

Incorrect:

processorXYZ <- ShapeProcessor()

Correct:

processor <- ShapeProcessor()

E05240 — A class not in the 'allow only' list cannot implement the sealed trait. Hexagon is not listed in 'Shape allow only Circle, Square, Triangle' so it triggers E02030. See ek9 -h E02030 for details.

Incorrect:

Shape allow only Circle, Square

Correct:

Shape allow only Circle, Square, Triangle
Other ways to ask this
  • Does EK9 check exhaustive dispatch on sealed traits?
  • How do I get exhaustive matching in EK9?
  • What is the EK9 equivalent of Java sealed switch?

Coming from another language?

Java: sealed interfaces + switch expressions with exhaustive pattern matching (Java 21+). Kotlin: sealed classes/interfaces + when expression with exhaustive checking. Rust: enum variants + match expression with exhaustive checking. Scala: sealed traits + match expression. EK9: dispatchers on sealed traits with compile-time exhaustiveness checking.

Keywords: switch, permit, exhaustive, sealed, missing, allow, only, dispatcher, handler, complete, dispatch, closed, allow-only, migrate, match