What is dispatcher ambiguity and how is it detected?

← Dispatcher Validation · Ref: Q616

Dispatcher ambiguity (E05230) occurs when two or more handlers match an argument type at equal cost. EK9 uses a cost-based dispatch model and refuses to guess when costs are tied.

COST MODEL

EK9 assigns dispatch costs based on type distance: exact type match costs 0.00, each class inheritance step costs 0.05, and each trait implementation step costs 0.10. The handler with the lowest cost wins.

WHEN AMBIGUITY OCCURS

Ambiguity arises when a class implements two traits and both traits have handlers at the same cost. Since both trait implementations are at equal distance, the dispatcher cannot choose.

EXAMPLE

If Duck implements both Flyable and Swimmable, and the dispatcher has handlers for both traits at the same depth, dispatching a Duck is ambiguous (both handlers cost 0.10).

RESOLUTION STRATEGIES

Strategy 1 - Explicit handler: add a handler for the ambiguous concrete type (exact match at cost 0.00 wins). Strategy 2 - Bridge trait: create a combining trait that encompasses both competing traits, giving it a shorter path. Strategy 3 - Decomposed dispatchers: split into separate dispatchers, each handling one concern.

EK9 REFUSES TO GUESS

Unlike languages that pick 'first declared' or 'most recently added', EK9 treats ambiguity as a design error. The compiler requires structural resolution.

See Q617 for diamond trait ambiguity. See Q60 for dispatcher fundamentals. See Q255 for cost-based method resolution.

Example

defines module qa.dispatchervalidation.ambiguity

  defines class

    //CORRECT: No ambiguity because only one trait hierarchy is dispatched
    Vehicle as abstract
      kind() as pure abstract
        <- rtn as String?
      default operator ?

    Car extends Vehicle
      override kind() as pure
        <- rtn as String: "car"
      default operator ?

    Truck extends Vehicle
      override kind() as pure
        <- rtn as String: "truck"
      default operator ?

    Motorcycle extends Vehicle
      override kind() as pure
        <- rtn as String: "motorcycle"
      default operator ?

    //Clear hierarchy: Car, Truck, Motorcycle all extend Vehicle
    //No ambiguity because single-parent inheritance has clear costs
    VehicleProcessor
      process() as dispatcher
        -> vehicle as Vehicle
        <- rtn as String: "Vehicle: " + vehicle.kind()

      process()
        -> vehicle as Car
        <- rtn as String: "Car: " + vehicle.kind()

      process()
        -> vehicle as Truck
        <- rtn as String: "Truck: " + vehicle.kind()

      process()
        -> vehicle as Motorcycle
        <- rtn as String: "Motorcycle: " + vehicle.kind()

  defines function

    testUnambiguousDispatch()
      processor <- VehicleProcessor()

      car <- Car()
      truck <- Truck()
      moto <- Motorcycle()

      require processor.process(car) == "Car: car"
      require processor.process(truck) == "Truck: truck"
      require processor.process(moto) == "Motorcycle: motorcycle"

Common mistakes

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

Incorrect:

require processor.process(car).toUpperCase() == "Car: car"

Correct:

require processor.process(car) == "Car: car"

E50010 — The dispatcher entry takes Vehicle. A handler for Shape is outside the Vehicle hierarchy and can never be reached through dispatch. See ek9 -h E50010 for details.

Incorrect:

process()
        -> shape as Shape

Correct:

process()
        -> vehicle as Truck
Other ways to ask this
  • What is E05230 in EK9?
  • When does a dispatcher become ambiguous?
  • How does EK9 resolve dispatcher cost ties?

Coming from another language?

Java: method overloading resolved at compile time, no runtime ambiguity concept. C++: multiple inheritance ambiguity resolved with virtual base classes. Python: MRO (C3 linearization) provides deterministic resolution. Kotlin: compiler error for ambiguous overloads. Julia: multiple dispatch with ambiguity detection. EK9: E05230 detects runtime dispatch ambiguity at compile time, requires structural resolution.

Keywords: dispatcher, explicit, trait, ambiguity, bridge, cost, E05230, validate, tie, resolution, dispatch