Persistent

Persistent data structures with structural sharing for efficient immutable updates. Based on Clojure-style persistent collections with O(log32 n) operations.

Structural Sharing

When you "modify" a persistent collection, the new version shares structure with the old version, making updates memory-efficient and enabling safe concurrent access without locks.

PVec (Persistent Vector)

Indexed collection with fast random access and updates at the end.

PVec.empty
PVec a
Creates an empty persistent vector.
PVec.append
PVec a -> a -> PVec a
Adds an element to the end. O(log32 n).
vec = PVec.empty |> PVec.append 1 |> PVec.append 2 |> PVec.append 3
PVec.to-list vec  # => [1, 2, 3]
PVec.get
PVec a -> Int -> Option a
Gets element at index. O(log32 n).
PVec.get vec 1  # => Some 2
PVec.get vec 99  # => None
PVec.set
PVec a -> Int -> a -> PVec a
Returns new vector with element at index replaced. O(log32 n).
vec2 = PVec.set vec 1 42
PVec.to-list vec2  # => [1, 42, 3]
PVec.to-list vec   # => [1, 2, 3] (unchanged)
PVec.pop
PVec a -> Option (PVec a)
Returns new vector with last element removed.
PVec.first / PVec.last
PVec a -> Option a
Get first or last element.
PVec.slice / PVec.take-n / PVec.drop-n
PVec a -> Int -> Int -> PVec a
Create subvectors efficiently.
PVec.concat
PVec a -> PVec a -> PVec a
Concatenate two vectors.
PVec.map / PVec.filter / PVec.fold
Standard functional operations.

PMap (Persistent HashMap)

Hash array mapped trie (HAMT) for efficient key-value storage.

PMap.empty
PMap k v
Creates an empty persistent map.
PMap.insert
PMap k v -> k -> v -> PMap k v
Returns new map with key-value pair added/updated.
m = PMap.empty
  |> PMap.insert "name" "Alice"
  |> PMap.insert "age" 30
PMap.get m "name"  # => Some "Alice"
PMap.get
PMap k v -> k -> Option v
Gets value for key. O(log32 n).
PMap.remove
PMap k v -> k -> PMap k v
Returns new map with key removed.
PMap.contains?
PMap k v -> k -> Bool
Check if key exists.
PMap.update / PMap.update-or
PMap k v -> k -> (v -> v) -> PMap k v
Update value with function. update-or provides default if key missing.
# Increment counter, starting at 0 if missing
m2 = PMap.update-or m "count" 0 (fn(n) => n + 1)
PMap.keys / PMap.values / PMap.entries
PMap k v -> [k] / [v] / [(k, v)]
Get keys, values, or key-value tuples as lists.
PMap.merge / PMap.merge-with
PMap k v -> PMap k v -> PMap k v
Merge maps. merge-with uses function to resolve conflicts.

PSet (Persistent HashSet)

Hash set with efficient membership testing and set operations.

PSet.empty
PSet a
Creates an empty persistent set.
PSet.insert / PSet.remove
PSet a -> a -> PSet a
Add or remove element.
s = PSet.empty |> PSet.insert 1 |> PSet.insert 2 |> PSet.insert 3
PSet.member? s 2  # => true
PSet.member?
PSet a -> a -> Bool
Check if element is in set.
PSet.union / PSet.intersection / PSet.difference
PSet a -> PSet a -> PSet a
Standard set operations.
a = PSet.from-list [1, 2, 3]
b = PSet.from-list [2, 3, 4]
PSet.to-list (PSet.intersection a b)  # => [2, 3]
PSet.subset? / PSet.superset? / PSet.disjoint?
PSet a -> PSet a -> Bool
Set relationship tests.

Transients

For building collections with many mutations, transients provide efficient mutable operations before converting back to persistent.

PVec.transient / PMap.transient / PSet.transient
P* a -> Transient a
Convert persistent collection to transient for mutation.
PVec.append! / PMap.insert! / PSet.insert!
Mutating operations on transients (faster than persistent equivalents).
PVec.persistent / PMap.persistent / PSet.persistent
Transient a -> P* a
Convert transient back to persistent. The transient should not be used after this.
# Build vector efficiently with transient
vec = PVec.empty
  |> PVec.transient
  |> PVec.append! 1
  |> PVec.append! 2
  |> PVec.append! 3
  |> PVec.persistent