What happens when a method comes from both a pure parent and a non-pure parent?
← Purity Contracts · Ref: Q1401
'pure' is always part of a method's contract, so an override is checked against EVERY parent that declares the method - the super class and each trait. When all of them are pure the override must be pure (E05150); when none are, it must not be (E05160). When they DISAGREE - a non-pure super and a pure trait, or two traits one way and the other - no single method can honour both contracts, and the class is rejected with E05290.
WHY
A caller holding the pure trait relies on the method having no side effects; a caller holding the non-pure super relies on it being free to have them. One body cannot be both.
FIX
Make the parents agree - mark both declarations 'pure', or neither - or stop combining them on one type.
This differs from 'sanitized', which an override may ADD but never drop: 'pure' must match every parent exactly.
See Q561 for pure overrides. See Q569 for traits and pure contracts. See Q663 for how 'sanitized' differs.
Example
defines module qa.purity.multipleparents defines trait <?- A pure contract: label() has no side effects. -?> Labelled label() as pure abstract <- rtn as String? defines class <?- The super declares label() pure too, so the parents agree. -?> Shape as open label() as pure <- rtn as String: "shape" default operator ? <?- Circle stands in for BOTH Shape.label() and Labelled.label() - it must be pure. -?> Circle extends Shape with trait of Labelled override label() as pure <- rtn as String: "circle" default operator ? defines program MultipleParentsDemo() stdout <- Stdout() asShape as Shape: Circle() asLabelled as Labelled: Circle() stdout.println(asShape.label()) stdout.println(asLabelled.label())
Common mistakes
E05290 — Shape.label() would no longer be pure while the trait Labelled.label() is, so Circle's override would have to honour two contracts that disagree. See ek9 -h E05290 for details.
Incorrect:
label()
<- rtn as String: "shape"
Correct:
label() as pure <- rtn as String: "shape"
Other ways to ask this
- What is E05290 parents disagree on pure?
- Can a class extend a non-pure super and implement a pure trait with the same method?
- Is 'pure' checked against every trait, or only the first parent?
- Why does my override conflict with two parents about purity?
Coming from another language?
Java: no purity in the type system - interfaces cannot promise side-effect freedom. Kotlin/Scala: no enforced purity. Haskell: purity is global (IO in the type), so the conflict cannot arise. EK9: 'pure' is part of every declaration's contract and the compiler rejects a type whose parents disagree on it.
Keywords: super, contract, parents, trait, override, pure, multiple, E05150, purity, conflict, E05290