How can trait diamond patterns cause dispatcher ambiguity?
← Dispatcher Validation · Ref: Q617
When a class implements two traits and a dispatcher has handlers for both traits, the dispatch cost to both handlers is identical (0.10 each). This creates ambiguity (E05230) because the compiler cannot determine which handler should win.
THE DIAMOND PROBLEM
Consider: Duck implements both Flyable and Swimmable. A dispatcher has handlers for Flyable (cost 0.10) and Swimmable (cost 0.10). When dispatching a Duck, both handlers are equally good matches. This is the diamond problem applied to dispatch.
BRIDGE TRAIT SOLUTION
Create a combining trait that encompasses both competing traits:
AmphibiousFlyer with trait of Flyable, Swimmable
Then Duck implements ONLY AmphibiousFlyer (not Flyable and Swimmable directly). The dispatcher handler for AmphibiousFlyer has cost 0.10, while Flyable and Swimmable handlers have cost 0.20 (two hops through the bridge). The bridge handler wins.
CRITICAL: the concrete type must implement ONLY the bridge trait, not the individual traits directly. If Duck implements all three, all handlers are at cost 0.10 and ambiguity remains.
EXPLICIT HANDLER SOLUTION
Add a handler for the specific ambiguous type (e.g., Duck) at cost 0.00 (exact match), which beats any trait handler.
See Q616 for ambiguity basics. See Q60 for dispatcher fundamentals. See Q107 for multiple trait implementation.
Example
defines module qa.dispatchervalidation.diamond defines trait //Bridge trait pattern to resolve diamond ambiguity Flyable canFly() as abstract <- rtn as Boolean? Swimmable canSwim() as abstract <- rtn as Boolean? //Bridge trait: combines both capabilities AmphibiousFlyer with trait of Flyable, Swimmable defines class //Duck implements ONLY the bridge trait (not Flyable/Swimmable directly) //This gives bridge handler cost 0.10, individual traits cost 0.20 Duck with trait of AmphibiousFlyer override canFly() <- rtn as Boolean: true override canSwim() <- rtn as Boolean: true //Eagle implements only Flyable Eagle with trait of Flyable override canFly() <- rtn as Boolean: true //Fish implements only Swimmable Fish with trait of Swimmable override canSwim() <- rtn as Boolean: true defines function testBridgeTrait() duck <- Duck() eagle <- Eagle() fish <- Fish() require duck.canFly() require duck.canSwim() require eagle.canFly() require fish.canSwim()
Common mistakes
E50060 — Boolean has no booleanValue() method in EK9. Boolean values are used directly in conditions. See ek9 -h E50060 for details.
Incorrect:
require duck.canFly().booleanValue()
Correct:
require duck.canFly()
E05120 — When implementing a trait abstract method, the override keyword is required. Omitting it on canFly triggers shadowing detection. See ek9 -h E05120 for details.
Incorrect:
canFly()
Correct:
override canFly()
Other ways to ask this
- What is the diamond problem in EK9 dispatchers?
- How do multiple trait implementations affect dispatch?
- Why does implementing two traits cause E05230?
Coming from another language?
Java: no runtime dispatch, diamond problem handled by interface default method rules. C++: virtual inheritance for diamond problem. Python: C3 linearization (MRO) provides deterministic resolution. Kotlin: explicit override resolution for diamond conflicts. Rust: no class inheritance, trait coherence rules. EK9: E05230 detects diamond dispatch ambiguity, resolved structurally via bridge traits or explicit handlers.
Keywords: trait, cost, validate, implement, multiple, diamond, dispatch, bridge, visitor, handler, E05230, sealed, ambiguity