How does purity interact with abstract function extension?
← Function Extension · Ref: Q595
If an abstract function is declared 'as pure', every implementation must also be 'as pure'. This is the same E05150 rule that applies to class method overrides. Purity is a contract from the abstract definition.
PURE ABSTRACT FUNCTION
When an abstract function is pure, the contract guarantees no side effects for all implementations:
Hasher as pure abstract -> input as String <- hash as Integer?
IMPLEMENTATIONS MUST BE PURE
Every function using 'is Hasher' must also be pure:
SimpleHasher is Hasher as pure -> input as String <- hash as Integer: #? input
CANNOT ADD PURITY
If the abstract function is NOT pure, implementations cannot add purity (E05160). Purity flows from the abstract definition.
WHY PURITY MATTERS FOR FUNCTIONS
Code using the abstract type expects consistent behavior. If some implementations are pure and others are not, callers cannot reason about side effects.
See Q560 for pure method basics. See Q561 for purity in overrides. See Q588 for function extension basics. See Q594 for extension rules.
Example
defines module qa.function.pureabstract defines function //Pure abstract: all implementations must be pure Scorer as pure abstract -> text as String <- score as Integer? LengthScorer is Scorer as pure -> text as String <- score as Integer: length text UpperScorer is Scorer as pure -> text as String <- score as Integer: length text.upperCase() //Non-pure abstract: implementations can be impure Reporter as abstract -> message as String <- report as String? SimpleReporter is Reporter -> message as String <- report as String: "[REPORT] " + message defines program PureAbstractFunctionDemo() stdout <- Stdout() //Pure function hierarchy scorers <- List() of Scorer scorers += LengthScorer scorers += UpperScorer for scorer in scorers stdout.println(`Score: ${scorer("Hello World")}`) //Non-pure function hierarchy reporter <- SimpleReporter stdout.println(reporter("system ready"))
Common mistakes
E05150 — When the abstract function is declared as pure, every implementation must also be pure. Omitting 'as pure' violates the purity contract. See ek9 -h E05150 for details.
Incorrect:
LengthScorer is Scorer -> text as String <- score as Integer: length text
Correct:
LengthScorer is Scorer as pure -> text as String <- score as Integer: length text
E50001 — A pure function cannot call non-pure methods like stdout.println(). The purity contract inherited from the abstract function forbids side effects. See ek9 -h E50001 for details.
Incorrect:
UpperScorer is Scorer as pure -> text as String <- score as Integer: length text.upperCase() stdout.println(score)
Correct:
UpperScorer is Scorer as pure -> text as String <- score as Integer: length text.upperCase()
Other ways to ask this
- Must implementations of a pure abstract function be pure?
- What is E05150 for function extension?
- How does purity flow through function hierarchies?
Coming from another language?
Java: no purity on functional interfaces. Kotlin: no purity on function types. Rust: Fn traits have no purity annotation. EK9: pure abstract functions enforce purity on all implementations via E05150.
Keywords: function, extension, pure, contract, side-effect, implementation, extend, abstract, E05150, purity, immutable