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
#!ek9 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: cost based, deep hierarchy, dispatcher, exact match, fallback, inheritance, open, walk up