If a parameter cannot be reassigned, is it read-only? How do aliasing and copying work in EK9?
← Functions and Methods · Ref: Q2000
EK9 keeps reference semantics: a variable is a BINDING to an object, and '<-' or '=' to another variable binds a second name to the SAME object. After 'shared <- original', 'shared++' changes what 'original' shows. For an independent value, construct one: 'independent <- Integer(original)' (or copy into an existing object with ':=:').
An incoming parameter's BINDING cannot be reassigned - 'counter := 0' on a parameter is E08110. That is all E08110 says. The parameter is NOT read-only: in non-pure code the object it refers to can be mutated in place ('counter += 1', 'items.add(x)') and the caller sees the change; it can also be aliased and the alias reassigned.
Read-only is what 'pure' provides. A pure function or method cannot mutate or reassign (E08100, E08120), and pure code may call only pure code (E08130), so the guarantee carries through everything it calls: 'doubled() as pure' may read its parameter but 'counter += 1' inside it is E08120.
Copying: ':=:' copies a value INTO an existing object. Each type copies at its own level - a value copies its value, a List copies its element references - and the generated 'default operator :=:' is SHALLOW: each field of the target then refers to the source's object. A deep or selective copy is an 'operator :=:' you write (see Q2100).
See Q737 for reassignment in pure code and Q905 for mutating operators and aliasing.
Example
defines module qa.functions.bindingsandobjects defines function bump() -> counter as Integer counter += 1 doubled() as pure -> counter as Integer <- rtn as Integer: Integer() rtn: counter * 2 defines program BindingsAndObjects() stdout <- Stdout() original <- 1 shared <- original shared++ stdout.println(`after shared++ original=${original} shared=${shared}`) independent <- Integer(original) independent++ stdout.println(`after independent++ original=${original} independent=${independent}`) bump(original) stdout.println(`after bump(original) original=${original}`) stdout.println(`doubled(original)=${doubled(original)} original=${original}`)
Common mistakes
E08110 — A parameter's binding cannot be reassigned. Mutate the caller's object in place with '+=', or work on a local copy ('working <- Integer(counter)'). See ek9 -h E08110 for details.
Incorrect:
counter := counter + 1
Correct:
counter += 1
E08120 — A pure function may not mutate anything - that is what makes its parameters read-only. Compute into the returning variable instead. See ek9 -h E08120 for details.
Incorrect:
counter += 1
Correct:
rtn: counter * 2
Other ways to ask this
- Does E08110 mean my function's parameters are read-only?
- Can a function change an object the caller passed in?
- Why does b++ also change a after b <- a?
- How do I get an independent copy of a value instead of an alias?
- What makes a parameter really read-only in EK9?
Coming from another language?
Java: parameters are references passed by value - reassigning one is legal but invisible to the caller, while mutating the object is visible; 'final' stops the reassignment only. Python: the same, with no 'final' at all. Kotlin: parameters are 'val' (no reassignment) but the object can still be mutated. C++: 'const T&' gives read-only access. EK9: the binding of a parameter is never reassignable (E08110); 'pure' is the read-only guarantee, enforced transitively.
Keywords: reassignment, alias, pure, E08120, binding, copy, aliasing, read-only, mutation, parameter, reference, E08110