Skip to content

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.

Start from what you are modeling, then reach for the matching module:

You need…UseNotes
An ordered list of plain valuesArrayBuilt-in JS arrays plus functional combinators.
A lazy or potentially infinite sequenceIterableWorks over any Symbol.iterator; transformations are lazy.
Keyed-object / tuple utilitiesRecord / Struct / TupleOperate on plain objects and tuples by key/position.
An immutable streaming sequenceChunkEfficient append/concat; the backbone of Stream.
A persistent map or set keyed by valueHashMap / HashSetStructural sharing; keys compared by Equal.
A map keyed by string prefixesTriePrefix search and ordered string-key iteration.
In-place mutable buffersMutableList / MutableHashMap / MutableHashSetFor hot loops and local accumulation.
A single mutable cellMutableRefOne boxed value you read and overwrite imperatively.
Nodes, edges, and graph algorithmsGraphDirected/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.

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 first
const a = Array.map(xs, (n) => n * 2)
// => [2, 4, 6]
// data-last: returns a unary function, ideal inside pipe
const 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)
// => 10

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)
// => 1

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

  • 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 with take.

    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 by Equal.

    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 like update and increment.

    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.