Can I apply a mutating operator like += to a method-call result?

← Operators and Expressions · Ref: Q1363

Yes - a MUTATING operator can be applied to a method-call result, but a plain or guarded ASSIGNMENT cannot.

MUTATING OPERATORS ON A CALL RESULT (ALLOWED)

EK9 methods return object references. A mutating operator (+=, -=, *=, /=, :~:, :^:, :=:) applied to a call result mutates the object the call returns IN PLACE:

  repository.items() += entry

This is exactly equivalent to repository.items()._addAss(entry) - the operator spelling of calling a mutating method on the returned object. It is NOT 'reassign the result of items()'; EK9's += never stores back, it always mutates the receiver.

WHEN THE MUTATION PERSISTS (AND WHEN IT DOES NOT)

Whether the mutation is observable depends entirely on WHAT the method returns:

  - Returns the internal field BY REFERENCE  -> the mutation persists. This is a deliberate 'here is my collection, modify it' exposure.
  - Returns a fresh COPY                     -> the mutation is applied to the throwaway copy and is lost.

The caller cannot tell which from the call site, so this is a property of the accessor's contract. It is the same aliasing consideration as any mutating method call on a result (e.g. getListCopy().add(x)).

SIMPLE / GUARDED ASSIGNMENT ON A CALL RESULT (REJECTED - E07895)
A simple (:=, :) or guarded (:=?) assignment needs a storage location to store into. A method-call result is a temporary value, not a location, so there is nothing to assign to:

  repository.items() := List()    // E07895: not an assignable target

Note this differs from assigning to a FIELD reached through a call, which targets a real field slot and IS allowed:

  getRecord().name := "Steve"      // OK - .name is an assignable field

See Q241 for mutation vs pure operators. See ek9 -h E07895 for the assignment restriction.

Example

defines module qa.operators.mutate.call.result

  defines class

    // Returns its internal collection BY REFERENCE: mutations through the accessor persist.
    Basket
      items <- List() of String

      items()
        <- rtn as List of String: items

      default operator ?

    // Returns a fresh COPY each call: mutations through the accessor are lost.
    SafeBasket
      contents <- List() of String

      items()
        <- rtn as List of String: cat contents | collect as List of String

      default operator ?

  defines program

    MutateCallResult()
      stdout <- Stdout()

      // Mutating operator on a call result that returns the internal field -> mutation PERSISTS
      basket <- Basket()
      basket.items() += "apple"
      basket.items() += "pear"
      stdout.println(`reference-return count: ${length basket.items()}`)

      // Same operator on a call result that returns a COPY -> mutation is LOST
      safe <- SafeBasket()
      safe.items() += "apple"
      stdout.println(`copy-return count: ${length safe.items()}`)

Common mistakes

E07895 — A mutating operator (+=, -=, :~:, :^:, :=:) on a method-call result mutates the returned object in place and is allowed. A simple (:=) or guarded (:=?) assignment has no location to store into - a call result is not an assignable target - so it triggers E07895. See ek9 -h E07895 for details.

Incorrect:

      basket.items() := List() of String

Correct:

      basket.items() += "apple"
Other ways to ask this
  • What does repository.items() += entry do?
  • Can I mutate the collection returned by a method?
  • Is assigning to a method-call result allowed in EK9?
  • Why does myObject.getList() += x compile but myObject.getList() := x not?

Coming from another language?

Java/Kotlin: obj.getList().add(x) mutates the returned list in place (same aliasing caveat); obj.getList() = x is a compile error (method call is not an lvalue). C++: obj.get() += x works if get() returns a reference (T&) and is meaningless/ill-formed otherwise. EK9: += on a call result is the operator form of a mutating method call and is always allowed; := / :=? on a call result is rejected at compile time (E07895) because there is no assignable target.

Keywords: reference, getter, assignable, E07895, accessor, lvalue, operator, mutating, in-place, method, call, list, collection, result, aliasing, return