What happens when I use mutating operators in EK9?

← Operators and Expressions · Ref: Q905

Mutating operators in EK9 return 'this' — the SAME object, not a copy. This creates aliasing, similar to C++ references or Java mutable objects.

MUTATING OPERATORS

These operators modify the object IN PLACE and return the same object:

  += -= *= /= — compound arithmetic
  ++ -- — increment/decrement
  :=: — copy (overwrites all fields)
  :^: — replace
  :~: — merge

ALIASING DANGER

  x <- 10
  y <- x++    ERROR — ++ is statement-only in EK9

EK9 prevents this by making ++ and -- STATEMENT-ONLY operators. You CANNOT use them in expressions.

CORRECT USAGE

  counter <- 0
  counter++         OK — statement
  counter += 5      OK — statement
  // To get a copy, use a separate declaration
  original <- 100
  copied <- original    creates independent copy
  copied++              only copied changes

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}`)

      // Copying creates independent values
      original <- 100
      copied <- original
      copied += 50
      stdout.println(`Original: ${original}, 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 <- original++

Correct:

copied <- original
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 are STATEMENT-ONLY — prevents aliasing bugs at compile time.

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