Create me a dispatcher for a deep class hierarchy (Shape -> Polygon -> Quadrilateral -> Square) where the Polygon handler catches Quadrilateral but a more specific Square handler exists.
← Dispatcher Validation · Ref: Q1244
Dispatchers select the handler whose parameter type most closely matches the runtime type of the argument, walking up the inheritance chain until a handler is found. This is COST-BASED dispatch — exact match has cost 0, parent has cost 1, grandparent cost 2, and so on.
DEEP INHERITANCE
Shape as abstract default operator ? Polygon is Shape as open default operator ? Quadrilateral is Polygon as open default operator ? Square is Quadrilateral default operator ?
Note 'as open' on intermediate classes — without it, EK9 closes the class and you cannot extend it further. Square is closed (default) because it is a leaf.
DISPATCHER WITH PARTIAL HANDLERS
Three handlers cover four types in the hierarchy:
GeometryRenderer
render() as dispatcher
-> shape as Shape
<- rtn as String: "Shape handler"
render()
-> shape as Polygon
<- rtn as String: "Polygon handler"
render()
-> shape as Square
<- rtn as String: "Square handler"
DISPATCH BEHAVIOUR
renderer.render(Polygon()) // "Polygon handler" (exact match) renderer.render(Quadrilateral()) // "Polygon handler" (Quadrilateral has no handler, walks up to Polygon) renderer.render(Square()) // "Square handler" (exact match wins over Polygon)
KEY INSIGHT
For Quadrilateral, the dispatcher walks up the hierarchy: no Quadrilateral handler -> try Polygon (found, use it). For Square, the exact handler exists so it wins even though Polygon is also a valid match (lower cost wins).
This avoids the visitor pattern boilerplate found in Java/C#: you only need handlers for the types you actually want to differentiate.
See Q620 for hierarchy dispatch rules. See Q616 for ambiguity detection. See Q1236 for sealed exhaustive dispatch.
Example
defines module qa.dispatcher.deephierarchy defines class Shape as abstract default operator ? Polygon is Shape as open default operator ? Quadrilateral is Polygon as open default operator ? Square is Quadrilateral default operator ? GeometryRenderer render() as dispatcher -> shape as Shape <- rtn as String: "Shape handler" render() -> shape as Polygon <- rtn as String: "Polygon handler" render() -> shape as Square <- rtn as String: "Square handler" defines program DeepHierarchyDispatchDemo() stdout <- Stdout() renderer <- GeometryRenderer() stdout.println(renderer.render(Polygon())) stdout.println(renderer.render(Quadrilateral())) stdout.println(renderer.render(Square()))
Common mistakes
E05030 — EK9 classes are CLOSED by default. To allow further extension (Quadrilateral extending Polygon), declare the parent 'as open'. Leaf classes can stay closed. See ek9 -h E05030 for details.
Incorrect:
Polygon is Shape
Correct:
Polygon is Shape as open
Other ways to ask this
- Show me how cost-based dispatch picks the closest handler in a deep inheritance tree.
- Write a dispatcher that demonstrates exact-match wins over superclass-match.
- Build a renderer that dispatches across a four-level hierarchy.
- Implement a dispatcher with handlers for some intermediate types but not all.
Coming from another language?
Java: visitor pattern with accept(visitor) on each subclass — must implement EVERY visit method. Scala: pattern matching on case classes — order matters, exhaustiveness optional. Kotlin: when expression with class checks — must have 'else' or sealed classes. Rust: match on enum variants — exhaustive by default. EK9: dispatcher walks the hierarchy by cost, with explicit fallback in the entry method's body.
Keywords: walk up, fallback, exact match, dispatcher, inheritance, open, cost based, deep hierarchy