Mutable Collections
Most of Effect’s collections (HashMap, HashSet, List, Chunk, …) are
persistent: every operation returns a new structure and leaves the original
untouched. That immutability is what makes them safe to share across fibers and
easy to reason about, but it has a cost — each update allocates.
The Mutable* modules trade that safety for raw speed. They mutate in place:
operations change the same instance you pass in (and often return it for
convenient piping), so a tight loop that accumulates thousands of values does not
allocate a fresh structure on every step.
When to use them
Section titled “When to use them”Reach for a mutable collection only for local, non-shared accumulation in hot loops, where the allocation of persistent structures dominates:
- building up a buffer or queue you will drain immediately,
- counting / deduplicating inside a single function,
- a private cache owned by one service.
Avoid them for shared state. They are not fiber-safe, carry no transactional guarantees, and aliasing one instance from two places means both observe the same changing data.
- For fiber-safe mutable state inside an
Effect, useRef— notMutableRef.MutableRefis a plain synchronous cell with no concurrency protection. - For shareable, persistent maps and sets, use the immutable
HashMap/HashSetmodules instead.
import { MutableList } from "effect"
// Accumulate work in a hot loop without allocating a new list each stepconst buffer = MutableList.make<number>()for (let i = 0; i < 10_000; i++) { MutableList.append(buffer, i) // mutates `buffer` in place, returns void}
console.log(buffer.length) // => 10000
// Drain it all at once into a plain arrayconst all = MutableList.takeAll(buffer)console.log(all.length) // => 10000console.log(buffer.length) // => 0 (the list is now empty)A MutableRef is the single-cell version — a stable reference whose .current
value changes over time. It is ideal for a local counter or flag:
import { MutableRef } from "effect"
const counter = MutableRef.make(0)
MutableRef.increment(counter) // returns the same refMutableRef.increment(counter)MutableRef.update(counter, (n) => n + 10)
console.log(MutableRef.get(counter)) // => 12The rest of this page is an exhaustive reference for the four mutable modules.
MutableList
Section titled “MutableList”A mutable linked list of array buckets, optimized for high-throughput
producer/consumer patterns. Values are appended/prepended at the ends and drained
from the head (FIFO). Every mutation updates the same instance and its length.
import { MutableList } from "effect"
const queue = MutableList.make<string>()MutableList.append(queue, "a")MutableList.append(queue, "b")console.log(MutableList.take(queue)) // => "a"interface MutableList
Section titled “interface MutableList”A stateful container exposing head, tail (internal buckets, treat as
implementation detail), and a length counter. Parameterized by element type.
import { MutableList } from "effect"
// Use the namespace type in annotationsconst drain = (list: MutableList.MutableList<number>): Array<number> => MutableList.takeAll(list)
const list = MutableList.make<number>()MutableList.appendAll(list, [1, 2, 3])console.log(list.length) // => 3console.log(drain(list)) // => [1, 2, 3]Empty (const)
Section titled “Empty (const)”A unique symbol returned by take when the list has no value. Compare
against it explicitly rather than relying on falsy checks (a stored 0, "", or
false is a valid value).
import { MutableList } from "effect"
const list = MutableList.make<string>()const result = MutableList.take(list)console.log(result === MutableList.Empty) // => trueEmpty (type)
Section titled “Empty (type)”The type of the Empty symbol, typeof MutableList.Empty. Used to type the
result of take as A | MutableList.Empty so callers handle the empty
case type-safely.
import { MutableList } from "effect"
const list = MutableList.make<number>()MutableList.append(list, 5)
const item: number | MutableList.Empty = MutableList.take(list)if (item !== MutableList.Empty) { console.log(item * 2) // => 10}Creates a new empty MutableList.
import { MutableList } from "effect"
const list = MutableList.make<number>()console.log(list.length) // => 0append
Section titled “append”Adds one element to the tail. Returns void and mutates in place; preserves FIFO
order with take.
import { MutableList } from "effect"
const list = MutableList.make<number>()MutableList.append(list, 1)MutableList.append(list, 2)console.log(MutableList.toArray(list)) // => [1, 2]prepend
Section titled “prepend”Adds one element to the head, so it is taken before existing contents (useful for
priority work or restoring an item to the front). Returns void.
import { MutableList } from "effect"
const list = MutableList.make<string>()MutableList.append(list, "normal")MutableList.prepend(list, "priority")console.log(MutableList.take(list)) // => "priority"prependAll
Section titled “prependAll”Prepends every element of an iterable to the head, in order, so the first element
of the iterable becomes the new head. Returns void.
import { MutableList } from "effect"
const list = MutableList.make<number>()MutableList.appendAll(list, [4, 5])MutableList.prependAll(list, [1, 2, 3])console.log(MutableList.takeAll(list)) // => [1, 2, 3, 4, 5]prependAllUnsafe
Section titled “prependAllUnsafe”Optimized prependAll for a ReadonlyArray. Pass mutable: true
only when you own the array’s lifecycle — the list may then reuse/modify it
internally for efficiency. Returns void.
import { MutableList } from "effect"
const list = MutableList.make<number>()MutableList.append(list, 4)
const items = [1, 2, 3]MutableList.prependAllUnsafe(list, items) // mutable defaults to falseconsole.log(items) // => [1, 2, 3] (unchanged)console.log(MutableList.takeAll(list)) // => [1, 2, 3, 4]appendAll
Section titled “appendAll”Appends every element of an iterable to the tail, preserving order, and returns the number of elements added.
import { MutableList } from "effect"
const list = MutableList.make<number>()const added = MutableList.appendAll(list, [1, 2, 3])console.log(added) // => 3console.log(MutableList.toArray(list)) // => [1, 2, 3]appendAllUnsafe
Section titled “appendAllUnsafe”Optimized appendAll for a ReadonlyArray, returning the number of
elements added. With mutable: true the list may reuse the array; only enable it
when you control that array.
import { MutableList } from "effect"
const list = MutableList.make<number>()const big = Array.from({ length: 1000 }, (_, i) => i)const added = MutableList.appendAllUnsafe(list, big, true) // very efficientconsole.log(added) // => 1000Empties the list in place, releasing internal memory, and resets length to 0.
Returns void. The same instance can be reused afterward.
import { MutableList } from "effect"
const list = MutableList.make<number>()MutableList.appendAll(list, [1, 2, 3])MutableList.clear(list)console.log(list.length) // => 0remove
Section titled “remove”Removes all occurrences of a value, in place, using JavaScript strict
equality (!==) — not Effect structural equality. Returns void.
import { MutableList } from "effect"
const list = MutableList.make<string>()MutableList.appendAll(list, ["a", "b", "a", "c", "a"])MutableList.remove(list, "a")console.log(MutableList.takeAll(list)) // => ["b", "c"]filter
Section titled “filter”Keeps only the elements satisfying the predicate (value, index) => boolean,
rebuilding the list in place. Returns void.
import { MutableList } from "effect"
const list = MutableList.make<number>()MutableList.appendAll(list, [1, 2, 3, 4, 5, 6])MutableList.filter(list, (n) => n % 2 === 0)console.log(MutableList.takeAll(list)) // => [2, 4, 6]Removes and returns the single head element, or the Empty
symbol if the list is empty.
import { MutableList } from "effect"
const list = MutableList.make<string>()MutableList.appendAll(list, ["first", "second"])console.log(MutableList.take(list)) // => "first"console.log(MutableList.take(list)) // => "second"console.log(MutableList.take(list) === MutableList.Empty) // => trueRemoves up to n elements from the head and returns them as an array (fewer if
the list is shorter). Includes zero-copy fast paths.
import { MutableList } from "effect"
const list = MutableList.make<number>()MutableList.appendAll(list, [1, 2, 3, 4, 5])console.log(MutableList.takeN(list, 3)) // => [1, 2, 3]console.log(MutableList.takeN(list, 10)) // => [4, 5]console.log(MutableList.takeN(list, 1)) // => []takeNVoid
Section titled “takeNVoid”Drops up to n elements from the head without returning them. If n <= 0 or
the list is empty it does nothing; if n >= length the list is cleared. Returns
void.
import { MutableList } from "effect"
const list = MutableList.make<number>()MutableList.appendAll(list, [1, 2, 3, 4, 5])MutableList.takeNVoid(list, 2)console.log(MutableList.toArray(list)) // => [3, 4, 5]takeAll
Section titled “takeAll”Removes and returns every element as an array, leaving the list empty. Equivalent
to takeN(list, list.length).
import { MutableList } from "effect"
const list = MutableList.make<string>()MutableList.appendAll(list, ["a", "b", "c"])console.log(MutableList.takeAll(list)) // => ["a", "b", "c"]console.log(list.length) // => 0toArray
Section titled “toArray”Copies all current elements into a new array without modifying the list — a
snapshot that leaves the list intact (contrast with takeAll).
import { MutableList } from "effect"
const list = MutableList.make<number>()MutableList.appendAll(list, [1, 2, 3])console.log(MutableList.toArray(list)) // => [1, 2, 3]console.log(list.length) // => 3 (still there)toArrayN
Section titled “toArrayN”Copies up to n elements from the head into a new array without modifying the
list. Use it to inspect a bounded prefix.
import { MutableList } from "effect"
const list = MutableList.make<number>()MutableList.appendAll(list, [10, 20, 30, 40])console.log(MutableList.toArrayN(list, 2)) // => [10, 20]console.log(list.length) // => 4 (unchanged)MutableHashMap
Section titled “MutableHashMap”An in-place key-value map. It pairs a native Map for ordinary JavaScript keys
with hash buckets for keys implementing Effect Equal / Hash, so structural and
referential keys can coexist. Mutating operations change the same instance and
return it for piping.
import { MutableHashMap } from "effect"
const map = MutableHashMap.empty<string, number>()MutableHashMap.set(map, "count", 1)MutableHashMap.set(map, "count", 2) // overwritesconsole.log(MutableHashMap.get(map, "count")) // => Option.some(2)Keys are matched with the same Equal / Hash rules as the immutable HashMap,
so values implementing Equal are compared structurally — mutating a
structural key after insertion can break future lookups if its hash changes.
interface MutableHashMap
Section titled “interface MutableHashMap”A mutable map of K to V, iterable as [key, value] pairs, Pipeable, and
Inspectable. Reports size in O(1).
import { MutableHashMap } from "effect"
const map: MutableHashMap.MutableHashMap<string, number> = MutableHashMap.make( ["a", 1], ["b", 2])console.log(Array.from(map)) // => [["a", 1], ["b", 2]]isMutableHashMap
Section titled “isMutableHashMap”Type guard narrowing an unknown value to MutableHashMap. Checks the runtime
marker only (not key/value types).
import { MutableHashMap } from "effect"
console.log(MutableHashMap.isMutableHashMap(MutableHashMap.empty())) // => trueconsole.log(MutableHashMap.isMutableHashMap(new Map())) // => falseCreates a fresh empty map. Each call returns a new instance.
import { MutableHashMap } from "effect"
const map = MutableHashMap.empty<string, number>()console.log(MutableHashMap.size(map)) // => 0Creates a map from explicit [key, value] entries known at the call site.
import { MutableHashMap } from "effect"
const map = MutableHashMap.make(["a", 1], ["b", 2], ["c", 3])console.log(MutableHashMap.size(map)) // => 3fromIterable
Section titled “fromIterable”Creates a map from any iterable of [key, value] pairs (arrays, native Map,
generators, …).
import { MutableHashMap } from "effect"
const map = MutableHashMap.fromIterable(new Map([["x", 10], ["y", 20]]))console.log(MutableHashMap.get(map, "x")) // => Option.some(10)Looks up a key, returning Option.some(value) if present or Option.none()
otherwise. Pipeable.
import { MutableHashMap } from "effect"
const map = MutableHashMap.make(["a", 1])console.log(MutableHashMap.get(map, "a")) // => Option.some(1)console.log(MutableHashMap.get(map, "z")) // => Option.none()Returns an iterable over the map’s keys.
import { MutableHashMap } from "effect"
const map = MutableHashMap.make(["a", 1], ["b", 2])console.log(Array.from(MutableHashMap.keys(map))) // => ["a", "b"]values
Section titled “values”Returns an iterable over the map’s values.
import { MutableHashMap } from "effect"
const map = MutableHashMap.make(["a", 1], ["b", 2])console.log(Array.from(MutableHashMap.values(map))) // => [1, 2]Tests whether a key is present, without reading its value. Pipeable.
import { MutableHashMap } from "effect"
const map = MutableHashMap.make(["a", 1])console.log(MutableHashMap.has(map, "a")) // => trueconsole.log(MutableHashMap.has(map, "z")) // => falseInserts a new entry or replaces an existing one, in place. Returns the map. Pipeable.
import { MutableHashMap } from "effect"
const map = MutableHashMap.empty<string, number>()MutableHashMap.set(map, "a", 1)MutableHashMap.set(map, "a", 99) // replacesconsole.log(MutableHashMap.get(map, "a")) // => Option.some(99)modify
Section titled “modify”Applies a function to the value of an existing key, in place. If the key is absent the map is unchanged. Returns the map. Pipeable.
import { MutableHashMap } from "effect"
const map = MutableHashMap.make(["count", 5])MutableHashMap.modify(map, "count", (n) => n + 1)console.log(MutableHashMap.get(map, "count")) // => Option.some(6)
MutableHashMap.modify(map, "missing", (n) => n + 1) // no effectconsole.log(MutableHashMap.has(map, "missing")) // => falsemodifyAt
Section titled “modifyAt”Updates, inserts, or removes a key from a function Option<V> => Option<V> of its
current optional value. Returning Option.none() removes the key. Returns the
map. Pipeable.
import { MutableHashMap, Option } from "effect"
const map = MutableHashMap.make(["count", 5])
// Insert when absentMutableHashMap.modifyAt(map, "new", (o) => Option.isNone(o) ? Option.some(42) : o)console.log(MutableHashMap.get(map, "new")) // => Option.some(42)
// Remove by returning NoneMutableHashMap.modifyAt(map, "count", () => Option.none())console.log(MutableHashMap.has(map, "count")) // => falseremove
Section titled “remove”Deletes one key in place; a no-op if the key is absent. Returns the map. Pipeable.
import { MutableHashMap } from "effect"
const map = MutableHashMap.make(["a", 1], ["b", 2])MutableHashMap.remove(map, "a")console.log(MutableHashMap.has(map, "a")) // => falseconsole.log(MutableHashMap.size(map)) // => 1Removes all entries in place, leaving the same map instance empty. Returns the map.
import { MutableHashMap } from "effect"
const map = MutableHashMap.make(["a", 1], ["b", 2])MutableHashMap.clear(map)console.log(MutableHashMap.size(map)) // => 0Returns the number of entries, in O(1).
import { MutableHashMap } from "effect"
const map = MutableHashMap.make(["a", 1], ["b", 2])console.log(MutableHashMap.size(map)) // => 2isEmpty
Section titled “isEmpty”Returns true when the map has no entries.
import { MutableHashMap } from "effect"
console.log(MutableHashMap.isEmpty(MutableHashMap.empty())) // => trueconsole.log(MutableHashMap.isEmpty(MutableHashMap.make(["a", 1]))) // => falseforEach
Section titled “forEach”Runs a callback for each entry. The callback receives (value, key) — value
first — matching Map.prototype.forEach. Pipeable.
import { MutableHashMap } from "effect"
const map = MutableHashMap.make(["a", 1], ["b", 2])MutableHashMap.forEach(map, (value, key) => { console.log(`${key} = ${value}`)})// => a = 1// => b = 2MutableHashSet
Section titled “MutableHashSet”An in-place set of unique values, built on MutableHashMap
(each value is stored as a key). Uniqueness follows the same Equal / Hash
rules, so structural values are de-duplicated structurally. Mutating operations
return the same set instance for piping.
import { MutableHashSet } from "effect"
const set = MutableHashSet.make("alice", "bob", "alice")console.log(MutableHashSet.size(set)) // => 2interface MutableHashSet
Section titled “interface MutableHashSet”A mutable collection of unique values of type V, iterable, Pipeable, and
Inspectable.
import { MutableHashSet } from "effect"
const set: MutableHashSet.MutableHashSet<number> = MutableHashSet.make(1, 2, 3)console.log(Array.from(set)) // => [1, 2, 3]isMutableHashSet
Section titled “isMutableHashSet”Type guard narrowing an unknown value to MutableHashSet. Native Set values
do not satisfy it.
import { MutableHashSet } from "effect"
console.log(MutableHashSet.isMutableHashSet(MutableHashSet.empty())) // => trueconsole.log(MutableHashSet.isMutableHashSet(new Set())) // => falseCreates a fresh empty set, backed by an empty MutableHashMap.
import { MutableHashSet } from "effect"
const set = MutableHashSet.empty<string>()console.log(MutableHashSet.size(set)) // => 0Creates a set from explicit values; duplicates are dropped.
import { MutableHashSet } from "effect"
const set = MutableHashSet.make("a", "b", "a", "c")console.log(MutableHashSet.size(set)) // => 3console.log(Array.from(set)) // => ["a", "b", "c"]fromIterable
Section titled “fromIterable”Creates a set from any iterable of values, dropping duplicates.
import { MutableHashSet } from "effect"
const set = MutableHashSet.fromIterable("hello")console.log(Array.from(set)) // => ["h", "e", "l", "o"]Inserts a value in place. Adding an existing value is a no-op. Returns the set. Pipeable.
import { MutableHashSet } from "effect"
const set = MutableHashSet.empty<string>()MutableHashSet.add(set, "apple")MutableHashSet.add(set, "apple") // duplicate, ignoredconsole.log(MutableHashSet.size(set)) // => 1Tests membership, using the underlying map’s hashing/equality rules. Pipeable.
import { MutableHashSet } from "effect"
const set = MutableHashSet.make("apple", "banana")console.log(MutableHashSet.has(set, "apple")) // => trueconsole.log(MutableHashSet.has(set, "grape")) // => falseremove
Section titled “remove”Deletes a value in place; a no-op if absent. Returns the set. Pipeable.
import { MutableHashSet } from "effect"
const set = MutableHashSet.make("a", "b", "c")MutableHashSet.remove(set, "b")console.log(MutableHashSet.has(set, "b")) // => falseconsole.log(MutableHashSet.size(set)) // => 2Returns the number of unique values.
import { MutableHashSet } from "effect"
const set = MutableHashSet.make(1, 2, 3, 2, 1)console.log(MutableHashSet.size(set)) // => 3Removes all values in place, leaving the same set instance empty. Returns the set.
import { MutableHashSet } from "effect"
const set = MutableHashSet.make("a", "b", "c")MutableHashSet.clear(set)console.log(MutableHashSet.size(set)) // => 0MutableRef
Section titled “MutableRef”A single mutable cell holding one current value, exposed through .current. Reads
and writes are synchronous. getAnd* helpers return the previous value;
*AndGet helpers return the new value; set/update/increment/toggle
return the same reference.
MutableRef is not fiber-safe and carries no transactional or effectful
guarantees. For state that must participate in Effect workflows, interruption,
or fiber coordination, use Ref instead.
import { MutableRef } from "effect"
const ref = MutableRef.make(42)console.log(ref.current) // => 42console.log(MutableRef.get(ref)) // => 42interface MutableRef
Section titled “interface MutableRef”A stable reference whose .current field of type T may change over time.
Pipeable and Inspectable; read or write .current directly, or use the
helpers below.
import { MutableRef } from "effect"
const ref: MutableRef.MutableRef<number> = MutableRef.make(1)ref.current = 100 // direct mutation worksconsole.log(MutableRef.get(ref)) // => 100Creates a new MutableRef initialized with a value.
import { MutableRef } from "effect"
const status = MutableRef.make("idle")console.log(MutableRef.get(status)) // => "idle"Reads the current value without mutating the reference.
import { MutableRef } from "effect"
const ref = MutableRef.make("hello")console.log(MutableRef.get(ref)) // => "hello"Replaces the current value in place and returns the same reference (not a copy). Pipeable.
import { MutableRef } from "effect"
const ref = MutableRef.make("a")const same = MutableRef.set(ref, "b")console.log(same === ref) // => trueconsole.log(MutableRef.get(ref)) // => "b"setAndGet
Section titled “setAndGet”Replaces the current value and returns the new value. Pipeable.
import { MutableRef } from "effect"
const ref = MutableRef.make("old")console.log(MutableRef.setAndGet(ref, "new")) // => "new"getAndSet
Section titled “getAndSet”Replaces the current value and returns the previous value. Pipeable.
import { MutableRef } from "effect"
const ref = MutableRef.make("old")console.log(MutableRef.getAndSet(ref, "new")) // => "old"console.log(MutableRef.get(ref)) // => "new"update
Section titled “update”Applies a function to the current value, storing the result in place, and returns the same reference. Pipeable.
import { MutableRef } from "effect"
const ref = MutableRef.make(5)MutableRef.update(ref, (n) => n * 2)console.log(MutableRef.get(ref)) // => 10updateAndGet
Section titled “updateAndGet”Applies a function to the current value and returns the new value. Pipeable.
import { MutableRef } from "effect"
const ref = MutableRef.make(5)console.log(MutableRef.updateAndGet(ref, (n) => n + 1)) // => 6getAndUpdate
Section titled “getAndUpdate”Applies a function to the current value and returns the previous value. Pipeable.
import { MutableRef } from "effect"
const ref = MutableRef.make(5)console.log(MutableRef.getAndUpdate(ref, (n) => n + 1)) // => 5console.log(MutableRef.get(ref)) // => 6compareAndSet
Section titled “compareAndSet”Sets newValue only if the current value equals oldValue, comparing with
Effect’s Equal.equals (structural, not just reference equality). Returns true
if it updated, false otherwise. Pipeable.
import { MutableRef } from "effect"
const ref = MutableRef.make("initial")console.log(MutableRef.compareAndSet(ref, "initial", "updated")) // => trueconsole.log(MutableRef.compareAndSet(ref, "initial", "again")) // => falseconsole.log(MutableRef.get(ref)) // => "updated"toggle
Section titled “toggle”Flips a boolean MutableRef between true and false, returning the same
reference.
import { MutableRef } from "effect"
const flag = MutableRef.make(false)MutableRef.toggle(flag)console.log(MutableRef.get(flag)) // => trueincrement
Section titled “increment”Adds 1 to a numeric MutableRef in place and returns the same reference.
import { MutableRef } from "effect"
const counter = MutableRef.make(5)MutableRef.increment(counter)console.log(MutableRef.get(counter)) // => 6incrementAndGet
Section titled “incrementAndGet”Adds 1 and returns the new value (pre-increment, like ++i).
import { MutableRef } from "effect"
const counter = MutableRef.make(5)console.log(MutableRef.incrementAndGet(counter)) // => 6getAndIncrement
Section titled “getAndIncrement”Adds 1 and returns the previous value (post-increment, like i++). Handy for
ID generation.
import { MutableRef } from "effect"
const ids = MutableRef.make(0)console.log(MutableRef.getAndIncrement(ids)) // => 0console.log(MutableRef.getAndIncrement(ids)) // => 1decrement
Section titled “decrement”Subtracts 1 from a numeric MutableRef in place and returns the same
reference.
import { MutableRef } from "effect"
const counter = MutableRef.make(5)MutableRef.decrement(counter)console.log(MutableRef.get(counter)) // => 4decrementAndGet
Section titled “decrementAndGet”Subtracts 1 and returns the new value.
import { MutableRef } from "effect"
const counter = MutableRef.make(5)console.log(MutableRef.decrementAndGet(counter)) // => 4getAndDecrement
Section titled “getAndDecrement”Subtracts 1 and returns the previous value.
import { MutableRef } from "effect"
const counter = MutableRef.make(5)console.log(MutableRef.getAndDecrement(counter)) // => 5console.log(MutableRef.get(counter)) // => 4