Why must all constructors be pure if any one is pure?
← Purity Contracts · Ref: Q564
EK9 enforces an all-or-nothing rule for constructor purity. If any constructor in a class is marked 'as pure', then every constructor must be marked 'as pure'. The compiler reports E05190 for violations.
WHY ALL OR NOTHING
Factory methods and delegation chains might call any constructor. If purity is inconsistent, the caller cannot know whether construction had side effects. Consistent purity makes object creation predictable.
THE ERROR: E05190
If one constructor is pure and another is not, the compiler reports E05190: mix of pure and not pure constructors.
CORRECT PATTERN: ALL PURE
Make every constructor pure if you want pure construction:
MyClass() as pure -> a as String ... MyClass() as pure -> a as String, b as Integer ...
CORRECT PATTERN: NONE PURE
Make no constructors pure if construction needs side effects:
MyClass()
-> a as String
...
MyClass()
-> a as String, b as Integer
...
See Q563 for pure constructor field assignment. See Q565 for multiple pure constructors. See Q586 for pure constructor delegation.
Example
defines module qa.purity.allornothing defines class //Correct: ALL constructors are pure Coordinate xPos as Float: Float() yPos as Float: Float() Coordinate() as pure -> xPos as Float yPos as Float this.xPos :=? xPos this.yPos :=? yPos //Second pure constructor: also uses :=? Coordinate() as pure -> xPos as Float this.xPos :=? xPos xPos() as pure <- rtn as Float: xPos yPos() as pure <- rtn as Float: yPos default operator ? //Correct: NO constructors are pure LoggedEntity entityName <- String() LoggedEntity() -> entityName as String this.entityName: entityName LoggedEntity() -> entityName as String prefix as String this.entityName: prefix + entityName entityName() as pure <- rtn as String: entityName default operator ? defines program AllOrNothingDemo() stdout <- Stdout() //All-pure construction coord1 <- Coordinate(3.0, 4.0) coord2 <- Coordinate(5.0) stdout.println(`Coord1: (${coord1.xPos()}, ${coord1.yPos()})`) stdout.println(`Coord2: (${coord2.xPos()}, ${coord2.yPos()})`) //No-pure construction entity1 <- LoggedEntity("server") entity2 <- LoggedEntity("server", "prod-") stdout.println(`Entity1: ${entity1.entityName()}`) stdout.println(`Entity2: ${entity2.entityName()}`)
Common mistakes
E05190 — Since the two-parameter Coordinate constructor is pure, the one-parameter constructor must also be pure. Mixing pure and non-pure constructors triggers E05190. See ek9 -h E05190 for details.
Incorrect:
Coordinate()
-> xPos as Float
Correct:
Coordinate() as pure -> xPos as Float
E50001 — Renaming the variable means later references to 'coord1' become unresolved, triggering E50001. See ek9 -h E50001 for details.
Incorrect:
coord1XYZ <- Coordinate(3.0, 4.0)
Correct:
coord1 <- Coordinate(3.0, 4.0)
Other ways to ask this
- What is E05190 mix of pure and not pure constructors?
- Can I have some pure and some non-pure constructors?
- What is the all-or-nothing constructor purity rule?
Coming from another language?
Java: no constructor purity concept. Python: no purity. Rust: constructors are always effectively pure. Kotlin: no constructor purity. EK9: all-or-nothing constructor purity enforced by E05190.
Keywords: immutable, purity, E05190, mix, constructor, pure, all-or-nothing, consistent, contract, factory, side-effect, constant