Mutation and copy-on-write

Meta: this is the page that prevents a lot of confusion. The thesis: methods look mutating but return a forked copy. Show the canonical Foo(42).incr.a == 43 example up front.

Values are immutable

The classic example

type Foo {
  pub a: Int!
  pub incr: Foo! {
    a += 1
    self
  }
}

Foo(42).incr.a == 43

What self.field = value actually does

Fork-per-call semantics

let c1 = Counter(0)
let c2 = c1.incr     # c2.value == 1
let c3 = c1.incr     # c3.value == 1, c1.value still 0

Within a method, mutations accumulate inside one fork

pub addAll(source: [String!]!): Builder! {
  source.each { item => self.items += [item] }
  self
}

Nested field assignment

Bare reassignment vs. field mutation

Meta: a diagram (boxes-and-arrows) would help a lot here. Even ASCII would do.

When not to think in CoW