What operations are forbidden inside a pure method?

← Data Flow Safety · Ref: Q635

Pure methods and functions are forbidden from using any mutation operator. This is enforced at compile time as E08120.

FORBIDDEN MUTATION OPERATORS IN PURE CONTEXT

The following operators are mutation operators and cannot appear inside a pure method or function: += (add-assign), -= (subtract-assign), *= (multiply-assign), /= (divide-assign), :=: (copy-into), :~: (merge-into), :^: (replace-with). Using any of these inside a pure method triggers E08120.

ALLOWED IN PURE CONTEXT

Reassignment with : (colon) is allowed because it creates a new binding. Non-mutating operators (+, -, *, /, ==, <>) are allowed. Creating new values and returning them is allowed. Reading fields and parameters is allowed.

WHY THIS RESTRICTION EXISTS

Pure methods guarantee no side effects. If a pure method could use +=, it would mutate the state of an existing object, breaking the purity contract. The compiler enforces this so callers can trust that pure methods do not change any state.

REASSIGNMENT VS MUTATION

Reassignment (variable: newValue) binds the variable to a new value. Mutation (variable += delta) modifies the existing object in place. Pure methods allow reassignment but forbid mutation.

See Q560 for pure method basics. See Q566 for pure call chain restrictions. See Q273 for purity as a security boundary.

Example

defines module qa.dataflow.puremutation

  defines class

    Account
      balance <- 0.0
      accountName <- "unnamed"

      Account()
        ->
          accountName as String
          initialBalance as Float
        this.accountName: accountName
        this.balance: initialBalance

      <?-
        Pure: computes new balance without mutating the field.
        Uses + operator (non-mutating) and : reassignment (allowed).
      -?>
      projectedBalance() as pure
        -> deposit as Float
        <- projected as Float: balance + deposit

      <?-
        Pure: builds a formatted string from fields.
        No mutation operators used.
      -?>
      formatSummary() as pure
        <- summary as String: `${accountName}: ${balance}`

      <?-
        Non-pure: uses mutation operator to modify balance.
        This method correctly omits 'as pure'.
      -?>
      deposit()
        -> amount as Float
        balance += amount

      <?-
        Non-pure: uses mutation operator.
      -?>
      withdraw()
        -> amount as Float
        balance -= amount

      default operator ?

  defines program

    PureMutationDemo()
      stdout <- Stdout()

      acct <- Account("Savings", 1000.0)
      stdout.println(acct.formatSummary())

      //Pure call: safe, no mutation
      projected <- acct.projectedBalance(500.0)
      stdout.println(`Projected: ${projected}`)

      //Non-pure: mutates the account
      acct.deposit(500.0)
      stdout.println(acct.formatSummary())

      acct.withdraw(200.0)
      stdout.println(acct.formatSummary())

Common mistakes

E50060 — String has no toUpperCase() method in EK9. Use upperCase() instead. See ek9 -h E50060 for details.

Incorrect:

stdout.println(acct.formatSummary().toUpperCase())

Correct:

stdout.println(acct.formatSummary())

E50001 — Removing the variable declaration means later references to the variable become unresolved, triggering E50001. See ek9 -h E50001 for details.

Incorrect:

acct.projectedBalance(500.0)

Correct:

projected <- acct.projectedBalance(500.0)
Other ways to ask this
  • Why does EK9 reject mutation operators in pure methods?
  • What is E08120 mutation in pure context?
  • Can I use += inside a pure function?

Coming from another language?

Java: no compile-time purity enforcement. Python: no immutability guarantees. Haskell: all functions pure by default. Rust: mutable borrows are explicit. Kotlin: val prevents reassignment but does not prevent method-level mutation. EK9: E08120 rejects mutation operators in pure methods at compile time.

Keywords: assign, restriction, migrate, mutation, side-effect, operator, E08120, immutable, function, merge, forbidden, pure, copy, safety, data-flow, initialize