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

#!ek9
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()
      require processor?

Correct:

processor <- ShapeProcessor()
      require processor?

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 E05240. See ek9 -h E05240 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: allow, allow-only, closed, complete, dispatch, dispatcher, exhaustive, handler, match, migrate, missing, only, permit, sealed, switch