Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Immutable Updates: assoc, dissoc, conj

Producing new values from existing ones. These macros expand to spread-based copies — no mutation, no runtime library.

assoc — Add or Replace Properties

(bind user (obj :name "Duncan" :age 42))
(bind updated (assoc user :age 43))
(bind extended (assoc user :age 43 :active true))
const user = {name: "Duncan", age: 42};
const updated = {...user, age: 43};
const extended = {...user, age: 43, active: true};

user is unchanged. updated and extended are new objects.

dissoc — Remove a Property

(bind safe (dissoc user :password))

The original object is not modified. safe is a new object without the :password key.

conj — Append to an Array

(bind items #a(1 2 3))
(bind more (conj items 4))
const items = [1, 2, 3];
const more = [...items, 4];

Shallow Copy

assoc uses spread (...), which is a shallow copy. Nested objects are shared, not deep-cloned. For nested updates:

(bind user (obj :name "Duncan" :address (obj :city "London")))
(bind moved (assoc user :address (assoc user:address :city "Paris")))

The outer assoc creates a new user. The inner assoc creates a new address. The original user and its original :address are both unchanged.

Keywords as Property Names

:age compiles to "age". camelCase conversion applies: (assoc user :first-name "D") produces {...user, firstName: "D"}. The same keywords used in obj construction and get access.