# Transactional collections (TxHashMap, TxHashSet, TxChunk)

A bare [`TxRef`](https://effect.plants.sh/transactions/transactional-state/) 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 — no `Effect.tx` needed.
- A read-modify-write that must be **atomic as a whole** (check-then-act,
  rename, transfer) belongs inside `Effect.tx`. Because each op is a `TxRef`
  read/write, they share one journal and commit together.

```ts
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 })
    })
  )
}
```
**Mutation vs. transformation:** Across all three modules, operations that "mutate" (`set`, `add`, `append`,
`filter`, `remove`, `clear`, …) update the *same* structure in place and return
`Effect<void>` or `Effect<boolean>`. Operations named like collection
*transforms* on `TxHashMap`/`TxHashSet` (`map`, `filterMap`, `compact`, …) build
and return a **new** `TxHashMap`/`TxHashSet`, leaving the original untouched.
Watch the per-method notes for exceptions: `TxHashMap.union` mutates in place
(returns `Effect<void>`), whereas `TxHashSet.union`/`intersection`/`difference`
return a new set. `TxChunk` is the other exception — its `map`/`filter` mutate in
place.

## TxHashMap

`TxHashMap<K, V>` is a transactional key-value map wrapping an immutable
[`HashMap`](https://effect.plants.sh/data-types/) 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:

```ts
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)

`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>`.

```ts
import type { TxHashMap } from "effect"

type M = TxHashMap.TxHashMap<string, number>
type K = TxHashMap.TxHashMap.Key<M> // => string
type V = TxHashMap.TxHashMap.Value<M> // => number
type E = TxHashMap.TxHashMap.Entry<M> // => readonly [string, number]
```

### make

Creates a map from inline key-value tuples.

```ts
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
})
```

### empty

Creates an empty map; supply the key/value types explicitly.

```ts
import { Effect, TxHashMap } from "effect"

const program = Effect.gen(function*() {
  const map = yield* TxHashMap.empty<string, number>()
  return yield* TxHashMap.isEmpty(map) // => true
})
```

### fromIterable

Builds a map from any iterable of `[key, value]` pairs (arrays, `Map`, etc.).

```ts
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

Type guard: `true` if the value is a `TxHashMap`.

```ts
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]
})
```

### get

Looks up a key, returning `Effect<Option<V>>`.

```ts
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

Like `get`, but uses a caller-supplied precomputed hash for the key (hot-path
optimization). The hash must correspond to the same key.

```ts
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" })
})
```

### has

Checks whether a key exists, returning `Effect<boolean>`.

```ts
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

`has` using a caller-supplied precomputed hash for the key.

```ts
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
})
```

### hasBy

Checks whether **any** entry satisfies a predicate over `(value, key)`.

```ts
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
})
```

### set

Inserts or overwrites the value for a key. Mutates in place; returns
`Effect<void>`.

```ts
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

Inserts/overwrites many entries from an iterable at once.

```ts
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

Applies `f` to an existing entry, returning the **previous** value in `Some`; if
the key is absent it returns `None` and writes nothing.

```ts
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

Updates, inserts, or removes a key via an `Option -> Option` function: returning
`Some` upserts the value, `None` removes any existing entry.

```ts
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

Removes a key, returning `Effect<boolean>` indicating whether it existed.

```ts
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

Removes every key in an iterable.

```ts
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
})
```

### clear

Removes all entries.

```ts
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
})
```

### size

Returns the number of entries.

```ts
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

Returns `true` when the map has no entries.

```ts
import { Effect, TxHashMap } from "effect"

const program = Effect.gen(function*() {
  const map = yield* TxHashMap.empty<string, number>()
  return yield* TxHashMap.isEmpty(map) // => true
})
```

### isNonEmpty

The complement of `isEmpty`.

```ts
import { Effect, TxHashMap } from "effect"

const program = Effect.gen(function*() {
  const map = yield* TxHashMap.make(["a", 1])
  return yield* TxHashMap.isNonEmpty(map) // => true
})
```

### keys

Returns an array of all keys.

```ts
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

Returns an array of all values.

```ts
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

Returns an array of all `[key, value]` pairs.

```ts
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

Alias for `entries`, for API parity with `HashMap`.

```ts
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

Alias for `values`, for API parity with `HashMap`.

```ts
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

Returns the current contents as an immutable `HashMap` — a stable copy you can
read with plain `HashMap` operations even as the `TxHashMap` keeps changing.

```ts
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

Runs an effectful function for each entry; returns `Effect<void>`.

```ts
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"
})
```

### map

Transforms every value (receiving `(value, key)`), returning a **new**
`TxHashMap`; the original is unchanged.

```ts
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

Keeps only entries whose `(value, key)` satisfy the predicate, returning a new
`TxHashMap`.

```ts
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

Filters and maps at once: `f` returns a [`Result`](https://effect.plants.sh/error-management/) —
`Result.succeed(x)` keeps the transformed value, `Result.failVoid` drops the
entry. Returns a new `TxHashMap`.

```ts
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

Given a map of `Option` values, drops the `None`s and unwraps the `Some`s into a
new `TxHashMap`.

```ts
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

Maps each entry to a `TxHashMap` (effectfully) and merges the results into a new
`TxHashMap`.

```ts
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

Folds all entries into a single value with `(acc, value, key) => acc`.

```ts
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

Returns the first `[key, value]` entry matching the predicate, as an `Option`.

```ts
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])
})
```

### some

`true` if at least one entry matches the predicate.

```ts
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
})
```

### every

`true` if all entries match the predicate (vacuously true when empty).

```ts
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
})
```

### union

Merges another **immutable** `HashMap` into this `TxHashMap`, with the other
map's values winning on key collisions. Mutates in place.

```ts
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

`TxHashSet<V>` is the transactional set, wrapping an immutable
[`HashSet`](https://effect.plants.sh/data-types/). 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:

```ts
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)

`TxHashSet<V>` exposes a `ref: TxRef<HashSet<V>>`. The namespace provides the
`TxHashSet.Value<T>` extractor.

```ts
import type { TxHashSet } from "effect"

type S = TxHashSet.TxHashSet<string>
type V = TxHashSet.TxHashSet.Value<S> // => string
```

### make

Creates a set from a variable list of values (duplicates collapse).

```ts
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
})
```

### empty

Creates an empty set.

```ts
import { Effect, TxHashSet } from "effect"

const program = Effect.gen(function*() {
  const set = yield* TxHashSet.empty<string>()
  return yield* TxHashSet.isEmpty(set) // => true
})
```

### fromIterable

Builds a set from any iterable.

```ts
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

Wraps an existing immutable `HashSet` in a transactional set.

```ts
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

Returns an immutable `HashSet` snapshot of the current contents.

```ts
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

Type guard for `TxHashSet`.

```ts
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]
})
```

### add

Adds a value (no-op if already present). Mutates in place.

```ts
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

Removes a value, returning `Effect<boolean>` for whether it was present.

```ts
import { Effect, TxHashSet } from "effect"

const program = Effect.gen(function*() {
  const set = yield* TxHashSet.make("a", "b")
  return yield* TxHashSet.remove(set, "b") // => true
})
```

### has

Membership check; uses `Equal`/`Hash` so custom value types work too.

```ts
import { Effect, TxHashSet } from "effect"

const program = Effect.gen(function*() {
  const set = yield* TxHashSet.make("a", "b")
  return yield* TxHashSet.has(set, "a") // => true
})
```

### size

Returns the number of values.

```ts
import { Effect, TxHashSet } from "effect"

const program = Effect.gen(function*() {
  const set = yield* TxHashSet.make("a", "b")
  return yield* TxHashSet.size(set) // => 2
})
```

### isEmpty

`true` when the set has no values.

```ts
import { Effect, TxHashSet } from "effect"

const program = Effect.gen(function*() {
  const set = yield* TxHashSet.empty<string>()
  return yield* TxHashSet.isEmpty(set) // => true
})
```

### clear

Removes all values. Mutates in place.

```ts
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
})
```

### map

Maps each value, returning a **new** `TxHashSet` (size may shrink if the function
produces duplicates).

```ts
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

Keeps only values satisfying the predicate, returning a new `TxHashSet`.

```ts
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

Folds all values into a single accumulator.

```ts
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
})
```

### some

`true` if at least one value matches the predicate.

```ts
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
})
```

### every

`true` if every value matches (vacuously true when empty).

```ts
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
})
```

### union

Returns a **new** `TxHashSet` containing values from both sets.

```ts
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

Returns a new `TxHashSet` of values present in **both** sets.

```ts
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

Returns a new `TxHashSet` of values in the first set but **not** the second.

```ts
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

`true` if every value of the first set is contained in the second.

```ts
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

`TxChunk<A>` is a transactional, ordered sequence backed by an immutable
[`Chunk`](https://effect.plants.sh/data-types/). 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.

```ts
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)

`TxChunk<A>` exposes a `ref: TxRef<Chunk<A>>`.

```ts
import type { TxChunk } from "effect"

type C = TxChunk.TxChunk<number>
```

### make

Creates a `TxChunk` from an existing immutable `Chunk`.

```ts
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]
})
```

### empty

Creates an empty `TxChunk`.

```ts
import { Effect, TxChunk } from "effect"

const program = Effect.gen(function*() {
  const c = yield* TxChunk.empty<number>()
  return yield* TxChunk.isEmpty(c) // => true
})
```

### fromIterable

Creates a `TxChunk` from any iterable.

```ts
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

Wraps an already-constructed `TxRef<Chunk<A>>` (advanced; no `Effect` needed).

```ts
import { Chunk, TxChunk, TxRef } from "effect"

const ref = TxRef.makeUnsafe(Chunk.fromIterable([1, 2, 3]))
const c = TxChunk.makeUnsafe(ref)
```

### get

Reads the current chunk; the access is tracked for conflict detection.

```ts
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]
})
```

### set

Replaces the entire stored chunk. Mutates in place.

```ts
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]
})
```

### size

Returns the element count.

```ts
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

`true` when the chunk has no elements.

```ts
import { Effect, TxChunk } from "effect"

const program = Effect.gen(function*() {
  const c = yield* TxChunk.empty<number>()
  return yield* TxChunk.isEmpty(c) // => true
})
```

### isNonEmpty

The complement of `isEmpty`.

```ts
import { Effect, TxChunk } from "effect"

const program = Effect.gen(function*() {
  const c = yield* TxChunk.fromIterable([1])
  return yield* TxChunk.isNonEmpty(c) // => true
})
```

### append

Appends one element to the end. Mutates in place.

```ts
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

Appends all elements of another immutable `Chunk` to the end.

```ts
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

Prepends one element to the beginning. Mutates in place.

```ts
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

Prepends all elements of another immutable `Chunk` to the beginning.

```ts
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

Appends the contents of another **`TxChunk`** to the end of this one (the other
is read transactionally and left unchanged).

```ts
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]
})
```

### take

Keeps only the first `n` elements. Mutates in place.

```ts
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]
})
```

### drop

Removes the first `n` elements. Mutates in place.

```ts
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]
})
```

### slice

Keeps elements from `start` (inclusive) to `end` (exclusive). Mutates in place.

```ts
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]
})
```

### map

Transforms each element with a function returning the **same** element type.
Mutates in place.

```ts
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

Keeps only elements satisfying the predicate. Mutates in place.

```ts
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

Atomically reads the current chunk and replaces it, returning a computed value —
the chunk equivalent of `TxRef.modify`. `f` returns `[returnValue, newChunk]`.

```ts
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

Replaces the stored chunk with `f(current)`, returning `Effect<void>` — like
`modify` without a return value.

```ts
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

| You need… | Use |
| --- | --- |
| A single atomic value | [`TxRef`](https://effect.plants.sh/transactions/transactional-state/) |
| Keyed lookup / registry | `TxHashMap` |
| Membership tracking | `TxHashSet` |
| Ordered, indexed buffer | `TxChunk` |
| Producer/consumer with blocking | [`TxQueue` / `TxPubSub`](https://effect.plants.sh/transactions/queues-and-pubsub/) |
| State you can subscribe to | [`TxSubscriptionRef`](https://effect.plants.sh/transactions/observable-state/) |
| Permits, locks, one-shot signals | [coordination primitives](https://effect.plants.sh/transactions/coordination/) |

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
`TxRef`s in a single atomic transaction.