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 {
  a: Int!
  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#

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#