What happens when I use mutating operators in EK9?

← Operators and Expressions · Ref: Q905

Mutating operators in EK9 change the object IN PLACE and return nothing (Void). Because they return nothing they are STATEMENT-ONLY: they cannot appear inside an expression.

MUTATING OPERATORS

These operators modify the object in place:

  - += -= *= /= — compound arithmetic
  - ++ -- — increment/decrement
  - :=: — copy another object's value into this one (each type at its own level; a List shares its elements)
  - :^: — replace
  - :~: — merge

ALIASING

Aliasing comes from '<-', not from the operator. Declaring a variable from another variable names the SAME object, so a mutating operator through either name is seen through both:

  x <- 10
  y <- x
  x++
  stdout.println(`${x} ${y}`)    // prints 11 11

And 'y <- x++' does not compile at all: ++ is statement-only (E07950).

CORRECT USAGE

  counter <- 0
  counter++
  counter += 5
  // For an independent copy, construct a NEW object
  original <- 100
  copied <- Integer(original)
  copied++
  stdout.println(`${original} ${copied}`)    // prints 100 101

NON-MUTATING OPERATORS

These create NEW objects and do NOT alias:

  - + - * / — arithmetic (return new value)
  - == <> < > <= >= — comparison (return Boolean)
  - <=> — three-way compare (return Integer)
  - $ — string conversion (return String)
  - #? — hashcode (return Integer)
  - #^ — promote (return wider type)
  - ? — isSet (return Boolean)

RULE: If the operator changes the object, it is mutating and statement-only. If it produces a new value, it is non-mutating and can be used in expressions.

See Q96 for all operators. See Q238 for operator enforcement.

Example

defines module qa.operators.mutating

  defines function

    <?-
      Shows correct use of mutating operators as statements.
      ++ -- += -= are STATEMENT-ONLY, not expressions.
    -?>
    accumulateTotal()
      -> numbers as List of Integer
      <- runningTotal as Integer: 0

      for n in numbers
        runningTotal += n

    <?-
      Shows non-mutating operators that create new values.
      + - * / return NEW objects, safe in expressions.
    -?>
    calculateAverage() as pure
      ->
        total as Integer
        divisor as Integer
      <-
        rtn as Float: 0.0

      if divisor > 0
        rtn := #^ total / #^ divisor

  defines program

    MutatingOperatorsDemo()
      stdout <- Stdout()

      // Mutating operators are STATEMENT-ONLY
      counter <- 0
      counter++
      counter++
      counter++
      stdout.println(`Counter after 3 increments: ${counter}`)

      counter += 10
      stdout.println(`Counter after += 10: ${counter}`)

      counter--
      stdout.println(`Counter after --: ${counter}`)

      // Non-mutating operators create NEW values (safe in expressions)
      productA <- 5 * 3
      productB <- 7 + 2
      stdout.println(`5 * 3 = ${productA}, 7 + 2 = ${productB}`)

      // <- from a variable names the SAME object, so += is seen through both names
      original <- 100
      sharedName <- original
      sharedName += 50
      stdout.println(`Shared: ${original}, ${sharedName}`)

      // An independent copy is a NEW object
      source <- 100
      copied <- Integer(source)
      copied += 50
      stdout.println(`Original: ${source}, Copied: ${copied}`)

      // Accumulation with +=
      numbers <- [10, 20, 30, 40]
      total <- accumulateTotal(numbers)
      avg <- calculateAverage(total, length numbers)
      stdout.println(`Total: ${total}, Average: ${avg}`)

Common mistakes

E07950 — The ++ operator is statement-only and cannot appear inside an expression; increment first, then use the value. See ek9 -h E07950 for details.

Incorrect:

copied <- source++

Correct:

copied <- Integer(source)
Other ways to ask this
  • Do mutating operators create aliases in EK9?
  • What does += return in EK9?
  • Why does y <- x make x and y the same object?
  • How do mutating operators work in EK9?

Coming from another language?

C++: mutating operators return references (aliasing risk). Java: += modifies in place, primitives are copied but objects are aliased. Rust: no implicit aliasing — ownership system prevents it. Go: no operator overloading. Python: += modifies in place for mutable types (lists), creates new for immutable (int). EK9: mutating operators change the object in place and return nothing, so they are STATEMENT-ONLY (no 'y <- x++' puzzles); '<-' from a variable shares the object, so construct a new one (Integer(x)) when you need an independent copy.

Keywords: expression, mutating, aliasing, copy, increment, compound, decrement, statement, operator