Why does EK9 reject reassignment in pure functions and parameter reassignment?
← Code Quality · Ref: Q737
EK9 enforces two reassignment restrictions to prevent hidden state changes.
PURE CONTEXT REASSIGNMENT (E08100)
In a pure function, ':=' on a local (non-returning) variable is rejected. Pure functions should compute results, not accumulate state. Two forms stay allowed: ':=?' (assign-if-unset, only sets if unset) and assignment to the return variable (rtn := start, or label: "excellent" as classifyScore does below). Mutating operators such as += are a separate ban in a pure scope (E08120), so they are not a way round E08100.
PARAMETER REASSIGNMENT (E08110)
You cannot REASSIGN a parameter (rawName := ...). The parameter names the caller's input, and pointing it at something else obscures the data flow. Instead, copy the parameter to a local variable and change the copy, as formatName does below.
Reassignment is not the same as mutation: in a non-pure function a parameter CAN be changed in place with a mutating operator (counterValue += 1, items += "x"), and the caller SEES that change, because the caller and the function share the same object.
WHY ENFORCED
Reassignment creates hidden state changes that are hard to reason about. Pure functions should be deterministic and side-effect free. Banning parameter reassignment keeps a parameter name meaning the caller's object for the whole body.
See Q678 for purity contracts. See Q635 for pure mutation restrictions.
Example
defines module qa.codequality.pureparamreassign defines function classifyScore() as pure -> score as Integer <- label as String: "average" highThreshold <- 90 lowThreshold <- 40 if score >= highThreshold label: "excellent" else if score < lowThreshold label: "poor" doubleValue() as pure -> input as Integer <- result as Integer: input * 2 formatName() -> rawName as String <- formatted as String: rawName localName <- rawName localName := "Mr. " + localName formatted: localName defines program PureParamReassignDemo() stdout <- Stdout() stdout.println(classifyScore(95)) stdout.println(classifyScore(30)) stdout.println(classifyScore(60)) stdout.println(`Doubled: ${doubleValue(21)}`) stdout.println(formatName("Smith"))
Common mistakes
E50060 — String has no toUpperCase() method in EK9. Use upperCase() instead. See ek9 -h E50060 for details.
Incorrect:
stdout.println(classifyScore(95).toUpperCase())
Correct:
stdout.println(classifyScore(95))
E50060 — String has no toUpperCase() method in EK9. Use upperCase() instead. See ek9 -h E50060 for details.
Incorrect:
stdout.println(formatName("Smith").toUpperCase())
Correct:
stdout.println(formatName("Smith"))
E08110 — Parameters cannot be reassigned with :=. Copy the parameter to a local variable first, then change the copy. See ek9 -h E08110 for details.
Incorrect:
rawName := "Mr. " + rawName
Correct:
localName <- rawName localName := "Mr. " + localName
E50060 — String has no toString() method. classifyScore() already returns a String. See ek9 -h E50060 for details.
Incorrect:
stdout.println(classifyScore(95).toString())
Correct:
stdout.println(classifyScore(95))
Other ways to ask this
- What is E08100 reassignment in pure context?
- What is E08110 parameter reassignment in EK9?
- How do I modify values in a pure function?
Coming from another language?
Java: parameters can be reassigned (but final prevents it). Kotlin: function parameters are val (immutable) by default. Rust: parameters are immutable by default, require mut. Python: parameters can be reassigned freely. Go: parameters can be reassigned freely. EK9: a parameter cannot be reassigned (E08110) but can be mutated in place outside pure scopes, visibly to the caller; a pure scope rejects ':=' on locals (E08100) but allows ':=?' and assigning the return variable, and rejects mutating operators (E08120).
Keywords: context, reassignment, quality, E08110, E08100, pure, parameter, restriction