How do pure constructors work and why use ':=?' for field assignment?
← Purity Contracts · Ref: Q563
Pure constructors use ':=?' (assign-if-unset) for field assignment, not ':' (direct assignment). The ':=?' operator only assigns if the variable is currently unset, preventing mutation.
WHY ':=?' IN PURE CONSTRUCTORS
In a pure constructor, assigning a field with ':' or ':=' is E08100, whatever the field's state (even an unset field). The ':=?' operator is allowed because it only assigns when the field is unset.
PATTERN FOR PURE CONSTRUCTORS
Declare fields with default values, then use ':=?' in the constructor:
Config
host as String: String()
port as Integer: Integer()
Config() as pure
->
host as String
port as Integer
this.host :=? host
this.port :=? port
WHY THIS WORKS
The fields must start UNSET - 'String()', 'Integer()' - and NOT a value such as 0 or "". A field given a value is already SET, so ':=?' can never fire and the constructor argument is silently discarded; the compiler rejects that as E08095. The ':=?' checks if the field is unset and assigns only then. After the first assignment, the field is set and ':=?' becomes a no-op. This is idempotent and non-mutating.
ALL OR NOTHING RULE
If ANY constructor is pure, ALL constructors must be pure (see Q564). This prevents inconsistent construction semantics.
See Q560 for pure method basics. See Q564 for all-or-nothing constructor purity. See Q565 for multiple pure constructors. See Q79 for assign-if-unset operator.
Example
defines module qa.purity.constructorassign defines class Config host as String: String() port as Integer: Integer() //Pure constructor using :=? for field assignment Config() as pure -> host as String port as Integer this.host :=? host this.port :=? port host() as pure <- rtn as String: host port() as pure <- rtn as Integer: port default operator ? //Another example: immutable value object Point xCoord as Float: Float() yCoord as Float: Float() Point() as pure -> xCoord as Float yCoord as Float this.xCoord :=? xCoord this.yCoord :=? yCoord xCoord() as pure <- rtn as Float: xCoord yCoord() as pure <- rtn as Float: yCoord distanceFromOrigin() as pure <- rtn as Float: sqrt(xCoord * xCoord + yCoord * yCoord) default operator ? defines program PureConstructorAssignDemo() stdout <- Stdout() config <- Config("localhost", 8080) stdout.println(`Host: ${config.host()}, Port: ${config.port()}`) point <- Point(3.0, 4.0) stdout.println(`Point: (${point.xCoord()}, ${point.yCoord()})`) stdout.println(`Distance: ${point.distanceFromOrigin()}`)
Common mistakes
E08095 — Giving the field a value at declaration makes it SET, so the ':=?' in the constructor can never apply and the 'host' argument is silently discarded - the object keeps "localhost" whatever you construct it with. Fields assigned by a pure constructor must be declared UNSET. See ek9 -h E08095 for details.
Incorrect:
host as String: "localhost"
Correct:
host as String: String()
E08100 — In a pure constructor, direct assignment with : counts as reassignment of a field. The assign-if-unset :=? must be used instead because it only assigns when the field is unset. See ek9 -h E08100 for details.
Incorrect:
this.host: host
Correct:
this.host :=? host
E50001 — A misspelt variable (pointXYZ) at its use does not resolve, triggering E50001. See ek9 -h E50001 for details.
Incorrect:
stdout.println(`Distance: ${pointXYZ.distanceFromOrigin()}`)
Correct:
stdout.println(`Distance: ${point.distanceFromOrigin()}`)
Other ways to ask this
- What is E08100 in a pure constructor?
- Why does my pure constructor reject ':' for field assignment?
- How do I assign fields in a pure constructor?
Coming from another language?
Java: no pure constructors, no assign-if-unset. Python: no purity concept. Rust: constructors are always pure (no mutation after move). Kotlin: no pure constructors. EK9: pure constructors with ':=?' for idempotent field initialization, E08100 prevents reassigning fields in a pure constructor.
Keywords: purity, immutable, constant, guarded, E08100, side-effect, mutation, safe, assign, contract, null-safe, isset, pure, constructor, idempotent, field