Why must a dispatcher method have a body in EK9?

← Classes and OOP · Ref: Q771

A dispatcher method routes calls to type-specific private overloads at runtime. The base dispatcher method MUST have a body — it serves as the default fallback when no specific overload matches the argument type.

DISPATCHER PATTERN

1. Declare a public method 'as dispatcher' taking a base type
2. Provide a body — this is the default handler
3. Add private overloads for specific types
4. Runtime routes to the most specific match

THIS EXAMPLE

The TypeProcessor class has a dispatcher method handle() that takes Any. The body returns a default description. Private overloads handle Integer and String specifically. The runtime automatically routes to the correct overload.

WHY A BODY IS REQUIRED

Unlike abstract methods (which have no body), dispatchers need a default implementation for the fallback case — when the argument type doesn't match any specific overload. Without this, the dispatcher has no behaviour for unmatched types.

COMMON MISTAKE

Developers assume dispatcher works like abstract — defining just the signature. But abstract means 'subclass provides implementation', while dispatcher means 'I route to overloads and handle the default case myself'.

See Q47 for dispatcher pattern. See Q51 for overloading vs dispatch. See Q768 for abstract methods (the opposite rule).

Example

defines module qa.classesandoop.dispatcherbody

  defines class

    TypeProcessor
      handle() as dispatcher
        -> item as Any
        <- rtn as String: "unhandled type"

      private handle()
        -> item as Integer
        <- rtn as String: `integer ${item}`

      private handle()
        -> item as String
        <- rtn as String: `string ${item}`

      default operator ?

    Describer
      describe() as dispatcher
        -> target as Any
        <- rtn as String: "unknown"

      private describe()
        -> target as Integer
        <- rtn as String: `number ${target}`

      default operator ?

  defines program

    ShowDispatcher()
      stdout <- Stdout()
      processor <- TypeProcessor()

      items <- [1, "hello", 3.14]
      for item in items
        result <- processor.handle(item)
        stdout.println(result)

      describer <- Describer()
      entries <- [99, true]
      for entry in entries
        output <- describer.describe(entry)
        stdout.println(output)

Common mistakes

E07120 — Removing the initialisation from the return variable leaves the dispatcher with no body. Dispatcher methods must have a default implementation for the fallback case. Provide a body that handles unmatched types. See ek9 -h E07120 for details.

Incorrect:

      handle() as dispatcher
        -> item as Any
        <- rtn as String?

Correct:

      handle() as dispatcher
        -> item as Any
        <- rtn as String: "unhandled type"

E07120 — The dispatcher describe() has no body — the return variable is declared but not initialised. Dispatchers are not abstract; they need a working default implementation. See ek9 -h E07120 for details.

Incorrect:

      describe() as dispatcher
        -> target as Any
        <- rtn as String?

Correct:

      describe() as dispatcher
        -> target as Any
        <- rtn as String: "unknown"
Other ways to ask this
  • What triggers E07120 dispatcher but no body provided?
  • How do I write a dispatcher method in EK9?
  • What is the dispatcher pattern in EK9?

Coming from another language?

Java: no built-in dispatch, manual instanceof chains or visitor pattern. Python: functools.singledispatch for function-level dispatch. Kotlin: no built-in multi-dispatch, use sealed class + when. Rust: no multi-dispatch, use enum + match. Go: no dispatch, use type switch. EK9: 'as dispatcher' keyword with automatic runtime type routing.

Keywords: runtime, method, default, routing, E07120, overload, dispatcher, type, body, dispatch