How do I write multiple pure constructors with delegation?
← Purity Contracts · Ref: Q565
Multiple pure constructors can delegate to each other using this(), just like non-pure constructors. The delegated-to constructor must also be pure (since all constructors must have consistent purity).
DELEGATION IN PURE CONSTRUCTORS
Pure constructors can call this() as the first statement:
Address() as pure -> street as String this(street, "Unknown") Address() as pure -> street as String, city as String this.street :=? street this.city :=? city
The delegating constructor calls another pure constructor.
WHY DELEGATION WORKS
Since E05190 requires all constructors to be consistently pure, the target of this() is guaranteed to be pure. No purity violation can occur.
PATTERN: DEFAULT VALUES
Use delegation to provide sensible defaults:
Config() as pure this("localhost", 8080) Config() as pure -> host as String this(host, 8080) Config() as pure -> host as String, port as Integer this.host :=? host this.port :=? port
See Q563 for pure constructor field assignment. See Q564 for all-or-nothing purity. See Q580 for this() delegation in general. See Q586 for pure constructor delegation with super().
Example
defines module qa.purity.multiplepure defines class Address street <- String() city <- String() postcode <- String() //Minimal constructor delegates to two-arg Address() as pure -> street as String this(street, "Unknown") //Two-arg delegates to three-arg Address() as pure -> street as String city as String this(street, city, "N/A") //Full constructor: assigns all fields Address() as pure -> street as String city as String postcode as String this.street :=? street this.city :=? city this.postcode :=? postcode street() as pure <- rtn as String: street city() as pure <- rtn as String: city postcode() as pure <- rtn as String: postcode default operator ? defines program MultiplePureConstructorsDemo() stdout <- Stdout() addr1 <- Address("123 High St") stdout.println(`${addr1.street()}, ${addr1.city()}, ${addr1.postcode()}`) addr2 <- Address("456 Low St", "London") stdout.println(`${addr2.street()}, ${addr2.city()}, ${addr2.postcode()}`) addr3 <- Address("789 Mid St", "Paris", "75001") stdout.println(`${addr3.street()}, ${addr3.city()}, ${addr3.postcode()}`)
Common mistakes
E05190 — All constructors must be consistently pure. If the full three-argument constructor is pure, the delegating one-argument constructor must also be declared pure. See ek9 -h E05190 for details.
Incorrect:
Address()
-> street as String
this(street, "Unknown")
Correct:
Address() as pure -> street as String this(street, "Unknown")
E08100 — In a pure constructor, direct assignment with : counts as reassignment. The guarded assignment :=? must be used for field initialization to maintain purity. See ek9 -h E08100 for details.
Incorrect:
this.street: street
Correct:
this.street :=? street
Other ways to ask this
- Can pure constructors use this() delegation?
- How do I chain pure constructors?
- What is the pattern for overloaded pure constructors?
Coming from another language?
Java: constructor chaining with this() works but no purity concept. Kotlin: constructor delegation but no purity enforcement. EK9: this() delegation in pure constructors, all constructors must be pure (E05190), :=? for field assignment.
Keywords: side-effect, contract, this, purity, constant, immutable, constructor, E05190, default, pure, overload, delegation, chain