Why must an override of a pure method also be pure?
← Purity Contracts · Ref: Q561
Note: override applies to both methods and operators (e.g., override operator ? as pure, override operator $ as pure).
When a parent method is marked 'as pure', every override of that method must also be marked 'as pure'. The compiler enforces this with error E05150.
WHY THIS RULE EXISTS
Callers of the parent type expect pure behavior. If a child could drop purity, polymorphic calls would break the contract:
parent as Shape: Circle() parent.area() // Caller expects pure, child must honour that
THE ERROR: E05150
If the parent method is pure and the child override is not pure, the compiler reports E05150: the parent method is pure so the override must also be pure.
CORRECT PATTERN
Always add 'as pure' to the override when the parent is pure:
override area() as pure <- rtn as Float: ...
BEHAVIORAL SUBSTITUTION
This enforces the Liskov Substitution Principle for purity. Anywhere a Parent is used, a Child can replace it without violating purity expectations.
See Q560 for pure method basics. See Q562 for the opposite rule (cannot add pure). See Q567 for purity through inheritance chains. See Q568 for pure operators and overrides.
Example
defines module qa.purity.overriderequired defines class Shape as abstract //Pure method in parent: all overrides must also be pure area() as pure abstract <- rtn as Float? describe() as pure abstract <- rtn as String? default operator ? Circle extends Shape radius <- 0.0 Circle() -> radius as Float this.radius: radius //Correct: override of pure method is also pure override area() as pure <- rtn as Float: radius * radius * 3.14159 override describe() as pure <- rtn as String: `Circle with radius ${radius}` default operator ? Square extends Shape side <- 0.0 Square() -> side as Float this.side: side //Correct: pure override matches parent purity override area() as pure <- rtn as Float: side * side override describe() as pure <- rtn as String: `Square with side ${side}` default operator ? defines program PureOverrideRequiredDemo() stdout <- Stdout() //Polymorphic use: callers trust pure contract shapes <- List() of Shape shapes += Circle(5.0) shapes += Square(4.0) for shape in shapes stdout.println(`${shape.describe()}: area = ${shape.area()}`)
Common mistakes
E05150 — The parent Shape declares area as pure abstract. Overriding without as pure violates the purity contract. All overrides of pure methods must also be pure. See ek9 -h E05150 for details.
Incorrect:
override area()
Correct:
override area() as pure
E05120 — When the parent Shape has an abstract describe method, the child must use the override keyword to replace it. Omitting override triggers shadowing detection. See ek9 -h E05120 for details.
Incorrect:
describe() as pure
Correct:
override describe() as pure
Other ways to ask this
- What is E05150 SUPER_IS_PURE?
- What happens if I override a pure method without 'as pure'?
- How does purity work with method overriding?
Coming from another language?
Java: no purity concept, method contracts are documentation only. Kotlin: no purity enforcement. Rust: trait methods have no purity marker. EK9: compiler-enforced purity inheritance via E05150.
Keywords: open, function, purity, override, child, parent, inheritance, abstract, immutable, pure, contract, substitution, E05150, side-effect, virtual