Transactional collections (TxHashMap, TxHashSet, TxChunk)
A bare TxRef holds a single value, but
shared state is often a collection: a registry of sessions, an inventory by
SKU, an ordered buffer. Effect ships transactional versions of the common data
structures — TxHashMap, TxHashSet, and TxChunk — each backed internally by
a TxRef, so every operation returns an Effect and participates in the same
transaction journal. That means you can read from a map, update a set, and
transform a chunk inside one Effect.tx block and have it all commit atomically
(or all retry together on conflict).
Two rules cover almost everything:
- A single operation (
TxHashMap.set,TxHashSet.add,TxChunk.append, …) is already its own transaction — noEffect.txneeded. - A read-modify-write that must be atomic as a whole (check-then-act,
rename, transfer) belongs inside
Effect.tx. Because each op is aTxRefread/write, they share one journal and commit together.
import { Context, Effect, Layer, Option, TxHashMap } from "effect"
// A shared inventory keyed by SKU. Multiple fibers reserve stock concurrently;// each reservation must check availability and decrement atomically.class Inventory extends Context.Service<Inventory, { readonly reserve: (sku: string, qty: number) => Effect.Effect<boolean> readonly stock: (sku: string) => Effect.Effect<number>}>()("app/Inventory") { static layer = Layer.effect(Inventory)( Effect.gen(function*() { const items = yield* TxHashMap.make(["laptop", 5], ["mouse", 20])
const reserve = Effect.fn("Inventory.reserve")(function*( sku: string, qty: number ) { // The read + check + write happen in one transaction, so two fibers // can never both pass the availability check for the last unit. return yield* Effect.tx( Effect.gen(function*() { const current = yield* TxHashMap.get(items, sku) if (Option.isNone(current) || current.value < qty) { return false } yield* TxHashMap.set(items, sku, current.value - qty) return true }) ) })
const stock = Effect.fn("Inventory.stock")(function*(sku: string) { const current = yield* TxHashMap.get(items, sku) return Option.getOrElse(current, () => 0) })
return Inventory.of({ reserve, stock }) }) )}TxHashMap
Section titled “TxHashMap”TxHashMap<K, V> is a transactional key-value map wrapping an immutable
HashMap in a TxRef. Reads that may be absent return an
Option. Here is the pattern that motivates the type — an atomic read-modify-write
across two keys:
import { Effect, TxHashMap } from "effect"
// Atomically rename a key: read the old value, set the new key, drop the old.// Wrapped in Effect.tx so no other fiber can observe the half-renamed state.const rename = ( map: TxHashMap.TxHashMap<string, number>, from: string, to: string) => Effect.tx( Effect.gen(function*() { const value = yield* TxHashMap.get(map, from) if (value._tag === "Some") { yield* TxHashMap.set(map, to, value.value) yield* TxHashMap.remove(map, from) } }) )TxHashMap (type)
Section titled “TxHashMap (type)”TxHashMap<K, V> is the map interface; it exposes a ref: TxRef<HashMap<K, V>>.
The companion namespace provides type extractors TxHashMap.Key<T>,
TxHashMap.Value<T>, and TxHashMap.Entry<T>.
import type { TxHashMap } from "effect"
type M = TxHashMap.TxHashMap<string, number>type K = TxHashMap.TxHashMap.Key<M> // => stringtype V = TxHashMap.TxHashMap.Value<M> // => numbertype E = TxHashMap.TxHashMap.Entry<M> // => readonly [string, number]Creates a map from inline key-value tuples.
import { Effect, TxHashMap } from "effect"
const program = Effect.gen(function*() { const map = yield* TxHashMap.make(["user1", "Alice"], ["user2", "Bob"]) return yield* TxHashMap.size(map) // => 2})Creates an empty map; supply the key/value types explicitly.
import { Effect, TxHashMap } from "effect"
const program = Effect.gen(function*() { const map = yield* TxHashMap.empty<string, number>() return yield* TxHashMap.isEmpty(map) // => true})fromIterable
Section titled “fromIterable”Builds a map from any iterable of [key, value] pairs (arrays, Map, etc.).
import { Effect, TxHashMap } from "effect"
const program = Effect.gen(function*() { const map = yield* TxHashMap.fromIterable(new Map([["a", 1], ["b", 2]])) return yield* TxHashMap.size(map) // => 2})isTxHashMap
Section titled “isTxHashMap”Type guard: true if the value is a TxHashMap.
import { Effect, TxHashMap } from "effect"
const program = Effect.gen(function*() { const map = yield* TxHashMap.make(["k", "v"]) return [TxHashMap.isTxHashMap(map), TxHashMap.isTxHashMap({})] // => [true, false]})Looks up a key, returning Effect<Option<V>>.
import { Effect, TxHashMap } from "effect"
const program = Effect.gen(function*() { const map = yield* TxHashMap.make(["alice", 30]) const a = yield* TxHashMap.get(map, "alice") // => Option.some(30) const b = yield* TxHashMap.get(map, "bob") // => Option.none()})getHash
Section titled “getHash”Like get, but uses a caller-supplied precomputed hash for the key (hot-path
optimization). The hash must correspond to the same key.
import { Effect, Hash, TxHashMap } from "effect"
const program = Effect.gen(function*() { const map = yield* TxHashMap.make(["session", { userId: "u1" }]) const v = yield* TxHashMap.getHash(map, "session", Hash.string("session")) // => Option.some({ userId: "u1" })})Checks whether a key exists, returning Effect<boolean>.
import { Effect, TxHashMap } from "effect"
const program = Effect.gen(function*() { const map = yield* TxHashMap.make(["alice", 1]) return yield* TxHashMap.has(map, "alice") // => true})hasHash
Section titled “hasHash”has using a caller-supplied precomputed hash for the key.
import { Effect, Hash, TxHashMap } from "effect"
const program = Effect.gen(function*() { const map = yield* TxHashMap.make(["admin", true]) return yield* TxHashMap.hasHash(map, "admin", Hash.string("admin")) // => true})Checks whether any entry satisfies a predicate over (value, key).
import { Effect, TxHashMap } from "effect"
const program = Effect.gen(function*() { const map = yield* TxHashMap.make(["a", 10], ["b", 3]) return yield* TxHashMap.hasBy(map, (v) => v > 5) // => true})Inserts or overwrites the value for a key. Mutates in place; returns
Effect<void>.
import { Effect, TxHashMap } from "effect"
const program = Effect.gen(function*() { const map = yield* TxHashMap.make(["laptop", 5]) yield* TxHashMap.set(map, "laptop", 3) return yield* TxHashMap.get(map, "laptop") // => Option.some(3)})setMany
Section titled “setMany”Inserts/overwrites many entries from an iterable at once.
import { Effect, TxHashMap } from "effect"
const program = Effect.gen(function*() { const map = yield* TxHashMap.empty<string, number>() yield* TxHashMap.setMany(map, [["a", 1], ["b", 2]]) return yield* TxHashMap.size(map) // => 2})modify
Section titled “modify”Applies f to an existing entry, returning the previous value in Some; if
the key is absent it returns None and writes nothing.
import { Effect, TxHashMap } from "effect"
const program = Effect.gen(function*() { const map = yield* TxHashMap.make(["downloads", 100]) const prev = yield* TxHashMap.modify(map, "downloads", (n) => n + 1) // => Option.some(100) (map now holds 101) const missing = yield* TxHashMap.modify(map, "views", (n) => n + 1) // => Option.none()})modifyAt
Section titled “modifyAt”Updates, inserts, or removes a key via an Option -> Option function: returning
Some upserts the value, None removes any existing entry.
import { Effect, Option, TxHashMap } from "effect"
const program = Effect.gen(function*() { const map = yield* TxHashMap.make(["count", 0]) yield* TxHashMap.modifyAt(map, "count", (cur) => Option.map(cur, (n) => n + 1)) return yield* TxHashMap.get(map, "count") // => Option.some(1)})remove
Section titled “remove”Removes a key, returning Effect<boolean> indicating whether it existed.
import { Effect, TxHashMap } from "effect"
const program = Effect.gen(function*() { const map = yield* TxHashMap.make(["a", 1]) const removed = yield* TxHashMap.remove(map, "a") // => true const again = yield* TxHashMap.remove(map, "a") // => false})removeMany
Section titled “removeMany”Removes every key in an iterable.
import { Effect, TxHashMap } from "effect"
const program = Effect.gen(function*() { const map = yield* TxHashMap.make(["a", 1], ["b", 2], ["c", 3]) yield* TxHashMap.removeMany(map, ["a", "b"]) return yield* TxHashMap.size(map) // => 1})Removes all entries.
import { Effect, TxHashMap } from "effect"
const program = Effect.gen(function*() { const map = yield* TxHashMap.make(["a", 1], ["b", 2]) yield* TxHashMap.clear(map) return yield* TxHashMap.isEmpty(map) // => true})Returns the number of entries.
import { Effect, TxHashMap } from "effect"
const program = Effect.gen(function*() { const map = yield* TxHashMap.make(["a", 1], ["b", 2]) return yield* TxHashMap.size(map) // => 2})isEmpty
Section titled “isEmpty”Returns true when the map has no entries.
import { Effect, TxHashMap } from "effect"
const program = Effect.gen(function*() { const map = yield* TxHashMap.empty<string, number>() return yield* TxHashMap.isEmpty(map) // => true})isNonEmpty
Section titled “isNonEmpty”The complement of isEmpty.
import { Effect, TxHashMap } from "effect"
const program = Effect.gen(function*() { const map = yield* TxHashMap.make(["a", 1]) return yield* TxHashMap.isNonEmpty(map) // => true})Returns an array of all keys.
import { Effect, TxHashMap } from "effect"
const program = Effect.gen(function*() { const map = yield* TxHashMap.make(["a", 1], ["b", 2]) return (yield* TxHashMap.keys(map)).sort() // => ["a", "b"]})values
Section titled “values”Returns an array of all values.
import { Effect, TxHashMap } from "effect"
const program = Effect.gen(function*() { const map = yield* TxHashMap.make(["a", 1], ["b", 2]) return (yield* TxHashMap.values(map)).sort() // => [1, 2]})entries
Section titled “entries”Returns an array of all [key, value] pairs.
import { Effect, TxHashMap } from "effect"
const program = Effect.gen(function*() { const map = yield* TxHashMap.make(["a", 1]) return yield* TxHashMap.entries(map) // => [["a", 1]]})toEntries
Section titled “toEntries”Alias for entries, for API parity with HashMap.
import { Effect, TxHashMap } from "effect"
const program = Effect.gen(function*() { const map = yield* TxHashMap.make(["a", 1]) return yield* TxHashMap.toEntries(map) // => [["a", 1]]})toValues
Section titled “toValues”Alias for values, for API parity with HashMap.
import { Effect, TxHashMap } from "effect"
const program = Effect.gen(function*() { const map = yield* TxHashMap.make(["a", 1], ["b", 2]) return (yield* TxHashMap.toValues(map)).sort() // => [1, 2]})snapshot
Section titled “snapshot”Returns the current contents as an immutable HashMap — a stable copy you can
read with plain HashMap operations even as the TxHashMap keeps changing.
import { Effect, HashMap, TxHashMap } from "effect"
const program = Effect.gen(function*() { const map = yield* TxHashMap.make(["temp", 22]) const snap = yield* TxHashMap.snapshot(map) yield* TxHashMap.set(map, "temp", 23) return HashMap.get(snap, "temp") // => Option.some(22) (snapshot frozen)})forEach
Section titled “forEach”Runs an effectful function for each entry; returns Effect<void>.
import { Console, Effect, TxHashMap } from "effect"
const program = Effect.gen(function*() { const map = yield* TxHashMap.make(["a", 1], ["b", 2]) yield* TxHashMap.forEach(map, (value, key) => Console.log(`${key}=${value}`)) // => logs "a=1" and "b=2"})Transforms every value (receiving (value, key)), returning a new
TxHashMap; the original is unchanged.
import { Effect, TxHashMap } from "effect"
const program = Effect.gen(function*() { const ages = yield* TxHashMap.make(["alice", 30], ["bob", 25]) const doubled = yield* TxHashMap.map(ages, (age) => age * 2) return yield* TxHashMap.get(doubled, "alice") // => Option.some(60)})filter
Section titled “filter”Keeps only entries whose (value, key) satisfy the predicate, returning a new
TxHashMap.
import { Effect, TxHashMap } from "effect"
const program = Effect.gen(function*() { const map = yield* TxHashMap.make(["a", 1], ["b", 5], ["c", 9]) const big = yield* TxHashMap.filter(map, (v) => v > 4) return yield* TxHashMap.size(big) // => 2})filterMap
Section titled “filterMap”Filters and maps at once: f returns a Result —
Result.succeed(x) keeps the transformed value, Result.failVoid drops the
entry. Returns a new TxHashMap.
import { Effect, Result, TxHashMap } from "effect"
const program = Effect.gen(function*() { const raw = yield* TxHashMap.make(["a", "30"], ["b", "x"]) const nums = yield* TxHashMap.filterMap(raw, (s) => { const n = parseInt(s) return isNaN(n) ? Result.failVoid : Result.succeed(n) }) return yield* TxHashMap.get(nums, "a") // => Option.some(30) ("b" dropped)})compact
Section titled “compact”Given a map of Option values, drops the Nones and unwraps the Somes into a
new TxHashMap.
import { Effect, Option, TxHashMap } from "effect"
const program = Effect.gen(function*() { const map = yield* TxHashMap.make( ["a", Option.some(1)], ["b", Option.none<number>()] ) const clean = yield* TxHashMap.compact(map) return yield* TxHashMap.size(clean) // => 1})flatMap
Section titled “flatMap”Maps each entry to a TxHashMap (effectfully) and merges the results into a new
TxHashMap.
import { Effect, TxHashMap } from "effect"
const program = Effect.gen(function*() { const teams = yield* TxHashMap.make(["eng", ["alice", "bob"]]) const people = yield* TxHashMap.flatMap(teams, (members, team) => Effect.gen(function*() { const out = yield* TxHashMap.empty<string, string>() for (const m of members) yield* TxHashMap.set(out, m, team) return out })) return yield* TxHashMap.get(people, "alice") // => Option.some("eng")})reduce
Section titled “reduce”Folds all entries into a single value with (acc, value, key) => acc.
import { Effect, TxHashMap } from "effect"
const program = Effect.gen(function*() { const sales = yield* TxHashMap.make(["q1", 100], ["q2", 200]) return yield* TxHashMap.reduce(sales, 0, (total, amt) => total + amt) // => 300})findFirst
Section titled “findFirst”Returns the first [key, value] entry matching the predicate, as an Option.
import { Effect, TxHashMap } from "effect"
const program = Effect.gen(function*() { const map = yield* TxHashMap.make(["a", 1], ["b", 9]) return yield* TxHashMap.findFirst(map, (v) => v > 5) // => Option.some(["b", 9])})true if at least one entry matches the predicate.
import { Effect, TxHashMap } from "effect"
const program = Effect.gen(function*() { const map = yield* TxHashMap.make(["a", 1], ["b", 9]) return yield* TxHashMap.some(map, (v) => v > 5) // => true})true if all entries match the predicate (vacuously true when empty).
import { Effect, TxHashMap } from "effect"
const program = Effect.gen(function*() { const map = yield* TxHashMap.make(["a", 2], ["b", 4]) return yield* TxHashMap.every(map, (v) => v % 2 === 0) // => true})Merges another immutable HashMap into this TxHashMap, with the other
map’s values winning on key collisions. Mutates in place.
import { Effect, HashMap, TxHashMap } from "effect"
const program = Effect.gen(function*() { const prefs = yield* TxHashMap.make(["theme", "light"]) yield* TxHashMap.union(prefs, HashMap.make(["theme", "dark"], ["lang", "en"])) return yield* TxHashMap.get(prefs, "theme") // => Option.some("dark")})TxHashSet
Section titled “TxHashSet”TxHashSet<V> is the transactional set, wrapping an immutable
HashSet. It is ideal for membership that several fibers update
— active connections, seen ids, a set of locked resources. The check-then-claim
pattern below is the canonical use:
import { Effect, TxHashSet } from "effect"
const program = Effect.gen(function*() { const active = yield* TxHashSet.empty<string>()
// Try to claim a slot only if it isn't already taken — atomically. const claim = (id: string) => Effect.tx( Effect.gen(function*() { if (yield* TxHashSet.has(active, id)) { return false } yield* TxHashSet.add(active, id) return true }) )
return { claimed: yield* claim("session-1"), again: yield* claim("session-1") } // => { claimed: true, again: false }})TxHashSet (type)
Section titled “TxHashSet (type)”TxHashSet<V> exposes a ref: TxRef<HashSet<V>>. The namespace provides the
TxHashSet.Value<T> extractor.
import type { TxHashSet } from "effect"
type S = TxHashSet.TxHashSet<string>type V = TxHashSet.TxHashSet.Value<S> // => stringCreates a set from a variable list of values (duplicates collapse).
import { Effect, TxHashSet } from "effect"
const program = Effect.gen(function*() { const set = yield* TxHashSet.make(1, 2, 2, 3) return yield* TxHashSet.size(set) // => 3})Creates an empty set.
import { Effect, TxHashSet } from "effect"
const program = Effect.gen(function*() { const set = yield* TxHashSet.empty<string>() return yield* TxHashSet.isEmpty(set) // => true})fromIterable
Section titled “fromIterable”Builds a set from any iterable.
import { Effect, TxHashSet } from "effect"
const program = Effect.gen(function*() { const set = yield* TxHashSet.fromIterable(["a", "b", "a"]) return yield* TxHashSet.size(set) // => 2})fromHashSet
Section titled “fromHashSet”Wraps an existing immutable HashSet in a transactional set.
import { Effect, HashSet, TxHashSet } from "effect"
const program = Effect.gen(function*() { const set = yield* TxHashSet.fromHashSet(HashSet.make("x", "y")) return yield* TxHashSet.size(set) // => 2})toHashSet
Section titled “toHashSet”Returns an immutable HashSet snapshot of the current contents.
import { Effect, HashSet, TxHashSet } from "effect"
const program = Effect.gen(function*() { const set = yield* TxHashSet.make("x", "y") const snap = yield* TxHashSet.toHashSet(set) return HashSet.size(snap) // => 2})isTxHashSet
Section titled “isTxHashSet”Type guard for TxHashSet.
import { Effect, HashSet, TxHashSet } from "effect"
const program = Effect.gen(function*() { const set = yield* TxHashSet.make(1) return [TxHashSet.isTxHashSet(set), TxHashSet.isTxHashSet(HashSet.make(1))] // => [true, false]})Adds a value (no-op if already present). Mutates in place.
import { Effect, TxHashSet } from "effect"
const program = Effect.gen(function*() { const set = yield* TxHashSet.make("a") yield* TxHashSet.add(set, "b") return yield* TxHashSet.size(set) // => 2})remove
Section titled “remove”Removes a value, returning Effect<boolean> for whether it was present.
import { Effect, TxHashSet } from "effect"
const program = Effect.gen(function*() { const set = yield* TxHashSet.make("a", "b") return yield* TxHashSet.remove(set, "b") // => true})Membership check; uses Equal/Hash so custom value types work too.
import { Effect, TxHashSet } from "effect"
const program = Effect.gen(function*() { const set = yield* TxHashSet.make("a", "b") return yield* TxHashSet.has(set, "a") // => true})Returns the number of values.
import { Effect, TxHashSet } from "effect"
const program = Effect.gen(function*() { const set = yield* TxHashSet.make("a", "b") return yield* TxHashSet.size(set) // => 2})isEmpty
Section titled “isEmpty”true when the set has no values.
import { Effect, TxHashSet } from "effect"
const program = Effect.gen(function*() { const set = yield* TxHashSet.empty<string>() return yield* TxHashSet.isEmpty(set) // => true})Removes all values. Mutates in place.
import { Effect, TxHashSet } from "effect"
const program = Effect.gen(function*() { const set = yield* TxHashSet.make("a", "b") yield* TxHashSet.clear(set) return yield* TxHashSet.isEmpty(set) // => true})Maps each value, returning a new TxHashSet (size may shrink if the function
produces duplicates).
import { Effect, TxHashSet } from "effect"
const program = Effect.gen(function*() { const set = yield* TxHashSet.make(1, 2, 3) const doubled = yield* TxHashSet.map(set, (n) => n * 2) return (Array.from(yield* TxHashSet.toHashSet(doubled))).sort() // => [2, 4, 6]})filter
Section titled “filter”Keeps only values satisfying the predicate, returning a new TxHashSet.
import { Effect, TxHashSet } from "effect"
const program = Effect.gen(function*() { const set = yield* TxHashSet.make(1, 2, 3, 4) const evens = yield* TxHashSet.filter(set, (n) => n % 2 === 0) return yield* TxHashSet.size(evens) // => 2})reduce
Section titled “reduce”Folds all values into a single accumulator.
import { Effect, TxHashSet } from "effect"
const program = Effect.gen(function*() { const set = yield* TxHashSet.make(1, 2, 3, 4) return yield* TxHashSet.reduce(set, 0, (acc, n) => acc + n) // => 10})true if at least one value matches the predicate.
import { Effect, TxHashSet } from "effect"
const program = Effect.gen(function*() { const set = yield* TxHashSet.make(1, 2, 3) return yield* TxHashSet.some(set, (n) => n > 2) // => true})true if every value matches (vacuously true when empty).
import { Effect, TxHashSet } from "effect"
const program = Effect.gen(function*() { const set = yield* TxHashSet.make(2, 4, 6) return yield* TxHashSet.every(set, (n) => n % 2 === 0) // => true})Returns a new TxHashSet containing values from both sets.
import { Effect, TxHashSet } from "effect"
const program = Effect.gen(function*() { const a = yield* TxHashSet.make("a", "b") const b = yield* TxHashSet.make("b", "c") const u = yield* TxHashSet.union(a, b) return yield* TxHashSet.size(u) // => 3})intersection
Section titled “intersection”Returns a new TxHashSet of values present in both sets.
import { Effect, TxHashSet } from "effect"
const program = Effect.gen(function*() { const a = yield* TxHashSet.make("a", "b", "c") const b = yield* TxHashSet.make("b", "c", "d") const i = yield* TxHashSet.intersection(a, b) return yield* TxHashSet.size(i) // => 2})difference
Section titled “difference”Returns a new TxHashSet of values in the first set but not the second.
import { Effect, TxHashSet } from "effect"
const program = Effect.gen(function*() { const a = yield* TxHashSet.make("a", "b", "c") const b = yield* TxHashSet.make("b", "d") const d = yield* TxHashSet.difference(a, b) return yield* TxHashSet.size(d) // => 2 ("a" and "c")})isSubset
Section titled “isSubset”true if every value of the first set is contained in the second.
import { Effect, TxHashSet } from "effect"
const program = Effect.gen(function*() { const small = yield* TxHashSet.make("a", "b") const large = yield* TxHashSet.make("a", "b", "c") return yield* TxHashSet.isSubset(small, large) // => true})TxChunk
Section titled “TxChunk”TxChunk<A> is a transactional, ordered sequence backed by an immutable
Chunk. Use it as a transactional buffer or log when you need
indexed, ordered access rather than map/set semantics. Most operations mutate the
stored chunk in place (returning Effect<void>); get returns the whole current
Chunk and modify returns a value you compute.
import { Chunk, Effect, TxChunk } from "effect"
const program = Effect.gen(function*() { const buffer = yield* TxChunk.empty<string>()
// Two appends commit together: a reader never sees just "a". yield* Effect.tx( Effect.gen(function*() { yield* TxChunk.append(buffer, "a") yield* TxChunk.append(buffer, "b") }) )
const all = yield* TxChunk.get(buffer) return Chunk.toReadonlyArray(all) // => ["a", "b"]})TxChunk (type)
Section titled “TxChunk (type)”TxChunk<A> exposes a ref: TxRef<Chunk<A>>.
import type { TxChunk } from "effect"
type C = TxChunk.TxChunk<number>Creates a TxChunk from an existing immutable Chunk.
import { Chunk, Effect, TxChunk } from "effect"
const program = Effect.gen(function*() { const c = yield* TxChunk.make(Chunk.fromIterable([1, 2, 3])) return Chunk.toReadonlyArray(yield* TxChunk.get(c)) // => [1, 2, 3]})Creates an empty TxChunk.
import { Effect, TxChunk } from "effect"
const program = Effect.gen(function*() { const c = yield* TxChunk.empty<number>() return yield* TxChunk.isEmpty(c) // => true})fromIterable
Section titled “fromIterable”Creates a TxChunk from any iterable.
import { Chunk, Effect, TxChunk } from "effect"
const program = Effect.gen(function*() { const c = yield* TxChunk.fromIterable([1, 2, 3]) return Chunk.toReadonlyArray(yield* TxChunk.get(c)) // => [1, 2, 3]})makeUnsafe
Section titled “makeUnsafe”Wraps an already-constructed TxRef<Chunk<A>> (advanced; no Effect needed).
import { Chunk, TxChunk, TxRef } from "effect"
const ref = TxRef.makeUnsafe(Chunk.fromIterable([1, 2, 3]))const c = TxChunk.makeUnsafe(ref)Reads the current chunk; the access is tracked for conflict detection.
import { Chunk, Effect, TxChunk } from "effect"
const program = Effect.gen(function*() { const c = yield* TxChunk.fromIterable([1, 2, 3]) return Chunk.toReadonlyArray(yield* TxChunk.get(c)) // => [1, 2, 3]})Replaces the entire stored chunk. Mutates in place.
import { Chunk, Effect, TxChunk } from "effect"
const program = Effect.gen(function*() { const c = yield* TxChunk.fromIterable([1, 2, 3]) yield* TxChunk.set(c, Chunk.fromIterable([9, 9])) return Chunk.toReadonlyArray(yield* TxChunk.get(c)) // => [9, 9]})Returns the element count.
import { Effect, TxChunk } from "effect"
const program = Effect.gen(function*() { const c = yield* TxChunk.fromIterable([1, 2, 3]) return yield* TxChunk.size(c) // => 3})isEmpty
Section titled “isEmpty”true when the chunk has no elements.
import { Effect, TxChunk } from "effect"
const program = Effect.gen(function*() { const c = yield* TxChunk.empty<number>() return yield* TxChunk.isEmpty(c) // => true})isNonEmpty
Section titled “isNonEmpty”The complement of isEmpty.
import { Effect, TxChunk } from "effect"
const program = Effect.gen(function*() { const c = yield* TxChunk.fromIterable([1]) return yield* TxChunk.isNonEmpty(c) // => true})append
Section titled “append”Appends one element to the end. Mutates in place.
import { Chunk, Effect, TxChunk } from "effect"
const program = Effect.gen(function*() { const c = yield* TxChunk.fromIterable([1, 2, 3]) yield* TxChunk.append(c, 4) return Chunk.toReadonlyArray(yield* TxChunk.get(c)) // => [1, 2, 3, 4]})appendAll
Section titled “appendAll”Appends all elements of another immutable Chunk to the end.
import { Chunk, Effect, TxChunk } from "effect"
const program = Effect.gen(function*() { const c = yield* TxChunk.fromIterable([1, 2]) yield* TxChunk.appendAll(c, Chunk.fromIterable([3, 4])) return Chunk.toReadonlyArray(yield* TxChunk.get(c)) // => [1, 2, 3, 4]})prepend
Section titled “prepend”Prepends one element to the beginning. Mutates in place.
import { Chunk, Effect, TxChunk } from "effect"
const program = Effect.gen(function*() { const c = yield* TxChunk.fromIterable([2, 3]) yield* TxChunk.prepend(c, 1) return Chunk.toReadonlyArray(yield* TxChunk.get(c)) // => [1, 2, 3]})prependAll
Section titled “prependAll”Prepends all elements of another immutable Chunk to the beginning.
import { Chunk, Effect, TxChunk } from "effect"
const program = Effect.gen(function*() { const c = yield* TxChunk.fromIterable([3, 4]) yield* TxChunk.prependAll(c, Chunk.fromIterable([1, 2])) return Chunk.toReadonlyArray(yield* TxChunk.get(c)) // => [1, 2, 3, 4]})concat
Section titled “concat”Appends the contents of another TxChunk to the end of this one (the other
is read transactionally and left unchanged).
import { Chunk, Effect, TxChunk } from "effect"
const program = Effect.gen(function*() { const a = yield* TxChunk.fromIterable([1, 2]) const b = yield* TxChunk.fromIterable([3, 4]) yield* TxChunk.concat(a, b) return Chunk.toReadonlyArray(yield* TxChunk.get(a)) // => [1, 2, 3, 4]})Keeps only the first n elements. Mutates in place.
import { Chunk, Effect, TxChunk } from "effect"
const program = Effect.gen(function*() { const c = yield* TxChunk.fromIterable([1, 2, 3, 4, 5]) yield* TxChunk.take(c, 3) return Chunk.toReadonlyArray(yield* TxChunk.get(c)) // => [1, 2, 3]})Removes the first n elements. Mutates in place.
import { Chunk, Effect, TxChunk } from "effect"
const program = Effect.gen(function*() { const c = yield* TxChunk.fromIterable([1, 2, 3, 4, 5]) yield* TxChunk.drop(c, 2) return Chunk.toReadonlyArray(yield* TxChunk.get(c)) // => [3, 4, 5]})Keeps elements from start (inclusive) to end (exclusive). Mutates in place.
import { Chunk, Effect, TxChunk } from "effect"
const program = Effect.gen(function*() { const c = yield* TxChunk.fromIterable([1, 2, 3, 4, 5, 6, 7]) yield* TxChunk.slice(c, 2, 5) return Chunk.toReadonlyArray(yield* TxChunk.get(c)) // => [3, 4, 5]})Transforms each element with a function returning the same element type. Mutates in place.
import { Chunk, Effect, TxChunk } from "effect"
const program = Effect.gen(function*() { const c = yield* TxChunk.fromIterable([1, 2, 3]) yield* TxChunk.map(c, (n) => n * 2) return Chunk.toReadonlyArray(yield* TxChunk.get(c)) // => [2, 4, 6]})filter
Section titled “filter”Keeps only elements satisfying the predicate. Mutates in place.
import { Chunk, Effect, TxChunk } from "effect"
const program = Effect.gen(function*() { const c = yield* TxChunk.fromIterable([1, 2, 3, 4, 5, 6]) yield* TxChunk.filter(c, (n) => n % 2 === 0) return Chunk.toReadonlyArray(yield* TxChunk.get(c)) // => [2, 4, 6]})modify
Section titled “modify”Atomically reads the current chunk and replaces it, returning a computed value —
the chunk equivalent of TxRef.modify. f returns [returnValue, newChunk].
import { Chunk, Effect, TxChunk } from "effect"
const program = Effect.gen(function*() { const c = yield* TxChunk.fromIterable([1, 2, 3]) const oldSize = yield* TxChunk.modify(c, (cur) => [ Chunk.size(cur), // => returned: 3 Chunk.append(cur, 4) // new stored value ]) // oldSize === 3; chunk now [1, 2, 3, 4]})update
Section titled “update”Replaces the stored chunk with f(current), returning Effect<void> — like
modify without a return value.
import { Chunk, Effect, TxChunk } from "effect"
const program = Effect.gen(function*() { const c = yield* TxChunk.fromIterable([1, 2, 3]) yield* TxChunk.update(c, (cur) => Chunk.reverse(cur)) return Chunk.toReadonlyArray(yield* TxChunk.get(c)) // => [3, 2, 1]})Choosing a structure
Section titled “Choosing a structure”| You need… | Use |
|---|---|
| A single atomic value | TxRef |
| Keyed lookup / registry | TxHashMap |
| Membership tracking | TxHashSet |
| Ordered, indexed buffer | TxChunk |
| Producer/consumer with blocking | TxQueue / TxPubSub |
| State you can subscribe to | TxSubscriptionRef |
| Permits, locks, one-shot signals | coordination primitives |
All of these follow the same pattern: every operation returns an Effect and
composes inside Effect.tx, so you can mix maps, sets, chunks, queues, and plain
TxRefs in a single atomic transaction.