Collections
Effect ships a large family of pure, value-oriented collection modules. They
play the role that lodash or Ramda do in a plain JavaScript codebase, but with
two important differences: the transforming operations are immutable (they
allocate new structures instead of mutating their inputs), and every module
exposes a dual API so the same function works both inside a pipe and as a
direct call.
import { Array, pipe } from "effect"
const numbers = [1, 2, 3, 4, 5]
// Pure, immutable transformations — `numbers` is never mutated.const doubledEvens = pipe( numbers, Array.filter((n) => n % 2 === 0), Array.map((n) => n * 2))
console.log(doubledEvens)// => [4, 8]console.log(numbers)// => [1, 2, 3, 4, 5] (unchanged)This page is a navigational hub. It explains how to pick a collection, how the dual APIs work, and how value equality factors in. Each module then has its own page with an exhaustive, example-driven reference.
Picking a collection
Section titled “Picking a collection”Start from what you are modeling, then reach for the matching module:
| You need… | Use | Notes |
|---|---|---|
| An ordered list of plain values | Array | Built-in JS arrays plus functional combinators. |
| A lazy or potentially infinite sequence | Iterable | Works over any Symbol.iterator; transformations are lazy. |
| Keyed-object / tuple utilities | Record / Struct / Tuple | Operate on plain objects and tuples by key/position. |
| An immutable streaming sequence | Chunk | Efficient append/concat; the backbone of Stream. |
| A persistent map or set keyed by value | HashMap / HashSet | Structural sharing; keys compared by Equal. |
| A map keyed by string prefixes | Trie | Prefix search and ordered string-key iteration. |
| In-place mutable buffers | MutableList / MutableHashMap / MutableHashSet | For hot loops and local accumulation. |
| A single mutable cell | MutableRef | One boxed value you read and overwrite imperatively. |
| Nodes, edges, and graph algorithms | Graph | Directed/undirected graphs with traversal and path finding. |
A useful rule of thumb: prefer the immutable modules (Array, Chunk,
HashMap, HashSet, Trie) for data that flows through your program, and the
Mutable* modules only as a local performance optimization where the mutation
cannot escape.
Dual APIs and pipe
Section titled “Dual APIs and pipe”Almost every function in these modules is dual: you can call it data-first
(passing the collection as the first argument) or data-last (omitting the
collection so it returns a function ready for pipe).
import { Array, pipe } from "effect"
const xs = [1, 2, 3]
// data-first: the collection comes firstconst a = Array.map(xs, (n) => n * 2)// => [2, 4, 6]
// data-last: returns a unary function, ideal inside pipeconst b = pipe(xs, Array.map((n) => n * 2))// => [2, 4, 6]Both forms produce the same result. Use data-last inside pipe when chaining
several steps, and data-first for one-off calls. See
Dual APIs for the full explanation and how to write
your own dual functions.
Because the modules share global names with the JavaScript built-ins (Array,
Record, String, Order, …), the Effect convention is to import the
namespace and reference members through it:
import { Array, Option } from "effect"
const first = Array.head([10, 20, 30])// => Option.some(10)
Option.getOrElse(first, () => 0)// => 10Value equality
Section titled “Value equality”The collection modules that deduplicate, look up, or compare elements use the
Equal protocol rather than JavaScript’s ===. This is what makes a
HashMap keyed by a structural value, or Array.dedupe, behave the way you
expect for data classes.
import { Array, Data } from "effect"
class Id extends Data.Class<{ readonly id: number }> {}
const a = new Id({ id: 1 })const b = new Id({ id: 1 })
// Data values are compared structurally via the Equal protocol.const unique = Array.dedupe([a, b])console.log(unique.length)// => 1Set-like operations such as Array.union, Array.intersection, and
Array.difference follow the same protocol, with *With variants when you
need domain-specific equality. See
Equal and Hash for how equality and hashing are
derived, and how to make your own types participate.
In this section
Section titled “In this section”-
Array— functional constructors, transforms, searches, folds, grouping, sorting, and set operations over JS arrays and non-empty arrays.import { Array } from "effect"Array.range(1, 3)// => [1, 2, 3] -
Iterable— lazy operations over any iterable, including unbounded sequences that you bound withtake.import { Iterable } from "effect"Array.from(Iterable.take(Iterable.range(1), 3))// => [1, 2, 3] -
Record/Struct/Tuple— keyed-object and tuple utilities for mapping, filtering, and reshaping plain objects and tuples. -
HashMap/HashSet— persistent, structurally-shared map and set with keys compared byEqual.import { HashMap } from "effect"const m = HashMap.make(["a", 1], ["b", 2])HashMap.get(m, "a")// => Option.some(1) -
Trie— a prefix tree mapping string keys to values, with prefix search. -
MutableList/MutableHashMap/MutableHashSet— in-place mutable structures for local, performance-sensitive accumulation. -
MutableRef— a single mutable cell with helpers likeupdateandincrement.import { MutableRef } from "effect"const ref = MutableRef.make(0)MutableRef.increment(ref)MutableRef.get(ref)// => 1 -
Graph— immutable directed/undirected graphs with traversal (dfs,bfs,topo), analysis, path finding (dijkstra,astar), and diagram export (toMermaid).import { Graph } from "effect"const g = Graph.directed<string, string>((mutable) => {const a = Graph.addNode(mutable, "A")const b = Graph.addNode(mutable, "B")Graph.addEdge(mutable, a, b, "A-B")})
Related: the immutable streaming sequence type Chunk
lives in the Data Types section, since it is primarily used as the building
block of Stream.