Xo is experimental. The source code is not public yet; it will be soon. Read the release notes
Menu · Values and mutation

Docs

Values and mutation

Everything in Xo is a value: assigning or passing a struct copies it (cheaply, because collections share structure). There are no pointers, so two parts of a program can never change the same value without synchronization, and data races cannot happen.

mutation/main.xo

struct Account { owner: Str, balance: Int }

fn (var a: Account) deposit(amount: Int) requires amount > 0 {
  a.balance += amount
}

fn main(os: Os) {
  var acct = Account{owner: "ada", balance: 10}
  let snapshot = acct
  acct.deposit(5)
  os.stdio.println("now ${acct.balance}, snapshot ${snapshot.balance}")

  var accounts: Map[Str, Account] = {"ada": acct}
  accounts.update("ada", Account{owner: "ada", balance: 0}, fn(var a) { a.deposit(100) })
  let stored = accounts.get("ada").map(fn(a) { a.balance }) ?? 0
  os.stdio.println("stored ${stored}")

  let hits = Atomic[Int].new(0)
  let cache = Mutex[Map[Str, Int]].new({})
  cache.with(fn(var m) { m.put("k", hits.add(1)) })
  let size = cache.with_read(fn(m) { m.len() })
  os.stdio.println("hits ${hits.get()}, cache ${size}")
}

Output:

now 15, snapshot 10
stored 115
hits 1, cache 1
  • snapshot kept the old balance: acct and snapshot are separate values.
  • A var receiver (fn (var a: Account)) changes the caller’s binding in place.
  • m.update(key, init, fn(var v) { ... }) changes a value inside a collection in one step. Copying a value out, changing it, and forgetting to store it back is a compile error (XO0520).
  • Mutex[T] and Atomic[T] are the only shared mutable state. with gives the closure the value to change; with_read gives read only access shared with other readers.

Closures capture by value, so they cannot quietly change a local:

fn main(os: Os) {
  var count = 0
  let bump = fn() { count += 1 }
  bump()
  os.stdio.println("${count}")
}

xo check says:

main.xo:3:21: error XO0502: assignment mutates `count`, which is captured by a closure; closures capture by value (core 3.3)
  note: main.xo:2:7: `count` is declared here, outside the closure
  note: to share mutable state with a closure, capture a `Mutex` or `Atomic` handle (core 5.4)
1 module(s), 1 error(s), 0 warning(s)