What happens when a dispatcher entry and handler have different purity?
← Dispatcher Validation · Ref: Q612
A dispatcher has ONE purity, set by its entry point, and every handler must match it. If the entry is 'as pure', every handler must also be 'as pure'. If the entry is not pure, no handler may be 'as pure'. Either mismatch raises E05170, and the message says which way it failed.
WHY THE HANDLERS MUST MATCH
Callers only ever see the entry point; the dispatcher routes their call to whichever handler matches the argument's runtime type. The entry point's purity is therefore the promise made about every handler. A non-pure handler would break a pure entry point's promise, and a pure handler behind a non-pure entry point is a claim no caller can rely on.
PURE DISPATCHER PATTERN
Mark the entry and all handlers as pure:
PureCatalog
describe() as pure dispatcher
-> item as Item
<- rtn as String: item.name()
describe() as pure -> item as Book <- rtn as String: "Book: " + item.name()
NON-PURE DISPATCHER PATTERN
Omit 'as pure' from the entry AND from every handler:
LoggingCatalog
label() as dispatcher
-> item as Item
<- rtn as String: item.name()
label()
-> item as Book
<- rtn as String: "Book: " + item.name()
BOTH DIRECTIONS ARE FORBIDDEN
You cannot have a pure entry with a non-pure handler, nor a non-pure entry with a pure handler. When a handler needs side effects, the whole dispatcher is not pure; when the whole dispatcher should be pure, every handler is.
See Q560 for purity basics. See Q566 for pure method restrictions. See Q60 for dispatcher fundamentals. See Q620 for dispatcher hierarchy rules.
Example
defines module qa.dispatchervalidation.purity defines class Item as abstract name() as pure abstract <- rtn as String? default operator ? Book extends Item title <- "untitled" Book() -> title as String this.title: title override name() as pure <- rtn as String: title default operator ? Magazine extends Item issue <- "unknown" Magazine() -> issue as String this.issue: issue override name() as pure <- rtn as String: issue default operator ? //CORRECT: pure dispatcher with all pure handlers PureCatalog describe() as pure dispatcher -> item as Item <- rtn as String: item.name() describe() as pure -> item as Book <- rtn as String: "Book: " + item.name() describe() as pure -> item as Magazine <- rtn as String: "Magazine: " + item.name() //CORRECT: a dispatcher that is not pure, with no pure handler LoggingCatalog labelled as Integer: 0 label() as dispatcher -> item as Item <- rtn as String? labelled++ rtn: item.name() label() -> item as Book <- rtn as String: "Book: " + item.name() default operator ? defines function testPureDispatcher() catalog <- PureCatalog() book <- Book("EK9 Guide") mag <- Magazine("Tech Monthly") bookResult <- catalog.describe(book) magResult <- catalog.describe(mag) require bookResult == "Book: EK9 Guide" require magResult == "Magazine: Tech Monthly" logging <- LoggingCatalog() require logging.label(book) == "Book: EK9 Guide" require logging.label(mag) == "Tech Monthly"
Common mistakes
E05170 — The dispatcher entry describe is declared as pure. Every handler must also be pure. Omitting as pure on a handler creates a purity mismatch. See ek9 -h E05170 for details.
Incorrect:
describe()
-> item as Book
Correct:
describe() as pure -> item as Book
E05170 — The dispatcher entry label is not pure, so no handler may be pure either. Marking one handler as pure creates a purity mismatch: callers only see the entry point, so the handler's promise is one they can never rely on. See ek9 -h E05170 for details.
Incorrect:
label() as pure -> item as Book
Correct:
label()
-> item as Book
E05210 — Each handler parameter type must be in the hierarchy of the dispatcher entry parameter type Item. A handler for an unrelated type like String can never be reached. See ek9 -h E05210 for details.
Incorrect:
describe() as pure -> item as String <- rtn as String: item
Correct:
describe() as pure -> item as Magazine <- rtn as String: "Magazine: " + item.name()
Other ways to ask this
- What is E05170 in EK9?
- Must dispatcher handlers match purity of the entry method?
- Can a pure dispatcher have non-pure handlers?
- Can a non-pure dispatcher have pure handlers?
Coming from another language?
Java: no purity concept, no dispatch purity checking. Kotlin: no purity enforcement on dispatchers. Haskell: all functions pure by default, dispatch via type classes. Rust: trait methods can be effectful or pure, but no dispatch purity contract. EK9: E05170 requires every handler to match the purity of the dispatcher entry point - all pure, or none pure.
Keywords: dispatch, entry, side-effect, E05170, dispatcher, pure, match, ambiguity, handler, contract, purity, immutable, validate, both-directions