# Chunk

`Chunk<A>` is an immutable, ordered sequence of values. It behaves like a
read-only array — you can map, filter, take, and index into it — but every
operation returns a new `Chunk` rather than mutating the original, and it
compares by *value* (`Equal.equals`) rather than by reference. Chunks are the
backing collection for [Streaming](https://effect.plants.sh/streaming/): a stream delivers its elements
in chunks, so you will see `Chunk` whenever you pull data out of a `Stream`.

```ts
import { Chunk } from "effect"

// Build a chunk and transform it — each step yields a fresh, immutable Chunk
const evensDoubled = Chunk.make(1, 2, 3, 4, 5, 6).pipe(
  Chunk.filter((n) => n % 2 === 0),
  Chunk.map((n) => n * 2)
)

// Convert back to a plain array at the boundary of your code
console.log(Chunk.toReadonlyArray(evensDoubled)) // [4, 8, 12]
```

Each `pipe` step produces a new `Chunk`; the original `make(1, …, 6)` is never
changed. When you need a normal array (to return from an API, say), convert with
`Chunk.toReadonlyArray`.

## Creating a Chunk

```ts
import { Chunk } from "effect"

const empty = Chunk.empty<number>()
const explicit = Chunk.make(1, 2, 3)               // from values
const fromArray = Chunk.fromIterable([1, 2, 3])    // from any Iterable
const single = Chunk.of(1)                          // one element
```

## Adding and combining

`append` / `prepend` add a single element; `appendAll` concatenates two chunks.
All of them return a new chunk.

```ts
import { Chunk } from "effect"

const base = Chunk.make(2, 3)

console.log(Chunk.toReadonlyArray(Chunk.prepend(base, 1))) // [1, 2, 3]
console.log(Chunk.toReadonlyArray(Chunk.append(base, 4)))  // [2, 3, 4]
console.log(
  Chunk.toReadonlyArray(Chunk.appendAll(base, Chunk.make(4, 5)))
) // [2, 3, 4, 5]
```

## Reading elements

`Chunk.get` returns an `Option` for safe indexing; `Chunk.head` and `Chunk.last`
are also `Option`-returning because a chunk may be empty. `Chunk.size` reports
the length.

```ts
import { Chunk } from "effect"

const chunk = Chunk.make(10, 20, 30)

console.log(Chunk.size(chunk))    // 3
console.log(Chunk.get(chunk, 1))  // { _id: 'Option', _tag: 'Some', value: 20 }
console.log(Chunk.get(chunk, 5))  // { _id: 'Option', _tag: 'None' }
console.log(Chunk.head(chunk))    // { _id: 'Option', _tag: 'Some', value: 10 }
```

Because out-of-bounds access yields `None` rather than `undefined`, you cannot
forget to handle the empty/missing case.

## Slicing and folding

The familiar collection operations are all here and all immutable:

```ts
import { Chunk } from "effect"

const chunk = Chunk.make(1, 2, 3, 4, 5)

console.log(Chunk.toReadonlyArray(Chunk.take(chunk, 2)))            // [1, 2]
console.log(Chunk.toReadonlyArray(Chunk.drop(chunk, 2)))           // [3, 4, 5]
console.log(Chunk.toReadonlyArray(Chunk.reverse(chunk)))           // [5, 4, 3, 2, 1]
console.log(Chunk.reduce(chunk, 0, (sum, n) => sum + n))           // 15
```

## Value equality

Two chunks built separately are equal when their elements are equal, so a
`Chunk` is safe to compare in tests and to use as a value inside other
structurally-compared types.

```ts
import { Chunk, Equal } from "effect"

console.log(Equal.equals(Chunk.make(1, 2, 3), Chunk.make(1, 2, 3))) // true
console.log(Equal.equals(Chunk.make(1, 2), Chunk.make(1, 2, 3)))    // false
```

## When to use Chunk

For everyday in-memory lists, a plain `ReadonlyArray` and the `Array` module are
perfectly fine. Reach for `Chunk` when you are working with
[Streaming](https://effect.plants.sh/streaming/) (where it is the native element container), or when you
want guaranteed immutability and structural equality for a sequence. Convert
with `Chunk.fromIterable` going in and `Chunk.toReadonlyArray` coming out.

### Why Chunk over Array?

A `Chunk` gives you two things a plain array does not:

- **Value equality via `Equal`.** Arrays compare by reference (`[1,2] !== [1,2]`),
  while `Equal.equals(Chunk.make(1, 2), Chunk.make(1, 2))` is `true`. This makes
  chunks safe keys/values inside `HashMap`, `HashSet`, and other
  structurally-compared types, and removes a class of test flakiness.
- **Fast concatenation, append, and prepend.** Internally a `Chunk` is a balanced
  tree of array segments, so `appendAll`, `append`, and `prepend` are cheap and
  share structure rather than copying. This is exactly why `Stream` accumulates
  and hands you data as chunks: building up output by concatenation is the hot
  path in streaming.

When you only need an in-memory list and don't care about either property, the
[`Array`](https://effect.plants.sh/data-types/array/) module on plain `ReadonlyArray` is lighter weight.

### NonEmptyChunk

`NonEmptyChunk<A>` is a `Chunk<A>` that the type system knows has at least one
element. Constructors like `make`, `of`, `makeBy`, and `range` return one, and
the `isNonEmpty` refinement narrows a `Chunk` to a `NonEmptyChunk` so that
"head" and "last" become *total* (non-`Option`) operations.

```ts
import { Chunk } from "effect"

const chunk: Chunk.Chunk<number> = Chunk.make(1, 2, 3)

if (Chunk.isNonEmpty(chunk)) {
  // inside this branch `chunk` is a NonEmptyChunk
  console.log(Chunk.headNonEmpty(chunk)) // 1  (a plain number, not Option)
  console.log(Chunk.lastNonEmpty(chunk)) // 3
}
```

Operations that *preserve* non-emptiness do so in their types: `map`, `reverse`,
`append`, `prepend`, and `appendAll`/`prependAll` (when one side is non-empty)
all return `NonEmptyChunk` from a `NonEmptyChunk` input.

---

# Reference

Every public `Chunk` export, grouped by purpose. All examples assume
`` (plus any extra modules shown). Many operations
return a new `Chunk`; we materialize results with `Chunk.toReadonlyArray` (`// =>`)
where the printed form matters.

## Types & guards

### Chunk

The core immutable sequence type, `Chunk<A>`. It is `Iterable`, `Equal`,
`Pipeable`, and `Inspectable`, and exposes a `.length`.

```ts
const chunk: Chunk.Chunk<number> = Chunk.make(1, 2, 3)
console.log(chunk.length) // => 3
```

### NonEmptyChunk

A `Chunk<A>` proven to hold at least one element; enables total head/last access.

```ts
const ne: Chunk.NonEmptyChunk<number> = Chunk.make(1, 2, 3)
console.log(Chunk.headNonEmpty(ne)) // => 1
```

### ChunkTypeLambda

The higher-kinded type lambda for `Chunk`, used when writing generic abstractions
over type constructors (HKT).

```ts
import type { Chunk, HKT } from "effect"

type NumberChunk = HKT.Kind<Chunk.ChunkTypeLambda, never, never, never, number>
// => Chunk<number>
```

### isChunk

Type guard: checks whether an unknown value is a `Chunk`.

```ts
console.log(Chunk.isChunk(Chunk.make(1, 2, 3))) // => true
console.log(Chunk.isChunk([1, 2, 3]))           // => false
```

### isEmpty

Returns `true` when the chunk has no elements.

```ts
console.log(Chunk.isEmpty(Chunk.empty()))      // => true
console.log(Chunk.isEmpty(Chunk.make(1)))      // => false
```

### isNonEmpty

Refinement: returns `true` when the chunk has at least one element and narrows it
to `NonEmptyChunk`.

```ts
const chunk = Chunk.make(1, 2)
if (Chunk.isNonEmpty(chunk)) {
  console.log(Chunk.headNonEmpty(chunk)) // => 1
}
```

### makeEquivalence

Builds an `Equivalence` for chunks from an element-level `Equivalence` (compares
length, then elements pairwise).

```ts
import { Chunk, Equivalence } from "effect"

const eq = Chunk.makeEquivalence(Equivalence.strictEqual<number>())
console.log(eq(Chunk.make(1, 2, 3), Chunk.make(1, 2, 3))) // => true
console.log(eq(Chunk.make(1, 2, 3), Chunk.make(1, 2, 4))) // => false
```

## Constructors

### empty

Creates an empty chunk.

```ts
console.log(Chunk.size(Chunk.empty<number>())) // => 0
```

### make

Builds a `NonEmptyChunk` from one or more inline values.

```ts
console.log(Chunk.toReadonlyArray(Chunk.make(1, 2, 3))) // => [1, 2, 3]
```

### of

Builds a single-element `NonEmptyChunk`.

```ts
console.log(Chunk.toReadonlyArray(Chunk.of("hello"))) // => ["hello"]
```

### fromIterable

Creates a chunk from any iterable (arrays, sets, generators, …). Returns the
input unchanged if it is already a `Chunk`.

```ts
console.log(Chunk.toReadonlyArray(Chunk.fromIterable(new Set([1, 2, 2, 3])))) // => [1, 2, 3]
```

### fromArrayUnsafe

Wraps an array into a chunk *without copying* — mutating the source array later
mutates the chunk. Use only when you own the array and won't mutate it.

```ts
const arr = [1, 2, 3]
const chunk = Chunk.fromArrayUnsafe(arr)
arr[0] = 999
console.log(Chunk.toReadonlyArray(chunk)) // => [999, 2, 3]
```

### fromNonEmptyArrayUnsafe

Like `fromArrayUnsafe`, but for a non-empty array; returns a `NonEmptyChunk`
without copying.

```ts
import { Array, Chunk } from "effect"

const chunk = Chunk.fromNonEmptyArrayUnsafe(Array.make(1, 2, 3))
console.log(Chunk.isNonEmpty(chunk)) // => true
```

### makeBy

Builds a `NonEmptyChunk` of length `n`, initializing each element from its index.

```ts
console.log(Chunk.toReadonlyArray(Chunk.makeBy(5, (i) => i * 2))) // => [0, 2, 4, 6, 8]
```

### range

Creates a `NonEmptyChunk` of consecutive integers from `start` to `end`
inclusive. If `start > end` it returns a single-element chunk of `start`.

```ts
console.log(Chunk.toReadonlyArray(Chunk.range(1, 5))) // => [1, 2, 3, 4, 5]
```

## Conversions

### toArray

Converts a chunk to a fresh, mutable `Array`. A `NonEmptyChunk` yields a
`NonEmptyArray`.

```ts
const arr = Chunk.toArray(Chunk.make(1, 2, 3))
console.log(arr)                // => [1, 2, 3]
console.log(Array.isArray(arr)) // => true
```

### toReadonlyArray

Converts a chunk to a `ReadonlyArray` (the canonical way to leave Chunk-land). A
`NonEmptyChunk` yields a `NonEmptyReadonlyArray`.

```ts
console.log(Chunk.toReadonlyArray(Chunk.make(1, 2, 3))) // => [1, 2, 3]
```

## Access

### get

Safely reads the element at an index, returning `Option`. Out-of-bounds yields
`None`.

```ts
import { Chunk, Option } from "effect"

const chunk = Chunk.make("a", "b", "c")
console.log(Chunk.get(chunk, 1))  // => Option.some("b")
console.log(Chunk.get(chunk, 10)) // => Option.none()
```

### getUnsafe

Reads the element at an index, throwing on out-of-bounds. Prefer `get` when the
index might be invalid.

```ts
console.log(Chunk.getUnsafe(Chunk.make("a", "b", "c"), 2)) // => "c"
```

### head

Returns the first element as an `Option`.

```ts
console.log(Chunk.head(Chunk.make(1, 2, 3))) // => Option.some(1)
console.log(Chunk.head(Chunk.empty()))       // => Option.none()
```

### headUnsafe

Returns the first element, throwing if the chunk is empty.

```ts
console.log(Chunk.headUnsafe(Chunk.make(1, 2, 3))) // => 1
```

### headNonEmpty

Total first-element access for a `NonEmptyChunk` (no `Option`, never throws).

```ts
console.log(Chunk.headNonEmpty(Chunk.make(1, 2, 3))) // => 1
```

### last

Returns the last element as an `Option`.

```ts
console.log(Chunk.last(Chunk.make(1, 2, 3))) // => Option.some(3)
console.log(Chunk.last(Chunk.empty()))       // => Option.none()
```

### lastUnsafe

Returns the last element, throwing if the chunk is empty.

```ts
console.log(Chunk.lastUnsafe(Chunk.make(1, 2, 3))) // => 3
```

### lastNonEmpty

Total last-element access for a `NonEmptyChunk`.

```ts
console.log(Chunk.lastNonEmpty(Chunk.make(1, 2, 3))) // => 3
```

### tail

Returns every element after the first, wrapped in `Option` (`None` if empty).

```ts
import { Chunk, Option } from "effect"

const t = Chunk.tail(Chunk.make(1, 2, 3))
console.log(Option.map(t, Chunk.toReadonlyArray)) // => Option.some([2, 3])
console.log(Chunk.tail(Chunk.empty()))            // => Option.none()
```

### tailNonEmpty

Returns every element after the first from a `NonEmptyChunk` (total; may be
empty).

```ts
console.log(Chunk.toReadonlyArray(Chunk.tailNonEmpty(Chunk.make(1, 2, 3)))) // => [2, 3]
```

### size

Returns the number of elements.

```ts
console.log(Chunk.size(Chunk.make(1, 2, 3))) // => 3
```

## Concatenation

### append

Adds one element to the end, returning a `NonEmptyChunk`.

```ts
console.log(Chunk.toReadonlyArray(Chunk.append(Chunk.make(1, 2), 3))) // => [1, 2, 3]
```

### prepend

Adds one element to the front, returning a `NonEmptyChunk`.

```ts
console.log(Chunk.toReadonlyArray(Chunk.prepend(Chunk.make(2, 3), 1))) // => [1, 2, 3]
```

### appendAll

Concatenates two chunks (`self` first). Non-emptiness of either input is
preserved in the type.

```ts
console.log(
  Chunk.toReadonlyArray(Chunk.appendAll(Chunk.make(1, 2), Chunk.make(3, 4)))
) // => [1, 2, 3, 4]
```

### prependAll

Concatenates `that` before `self` (the prefix comes first).

```ts
console.log(
  Chunk.make(1, 2).pipe(Chunk.prependAll(Chunk.make("a", "b")), Chunk.toReadonlyArray)
) // => ["a", "b", 1, 2]
```

## Slicing

### take

Keeps the first `n` elements.

```ts
console.log(Chunk.toReadonlyArray(Chunk.take(Chunk.make(1, 2, 3, 4, 5), 3))) // => [1, 2, 3]
```

### takeRight

Keeps the last `n` elements.

```ts
console.log(Chunk.toReadonlyArray(Chunk.takeRight(Chunk.make(1, 2, 3, 4, 5), 2))) // => [4, 5]
```

### takeWhile

Keeps the leading run of elements that satisfy a predicate. Accepts a refinement
to narrow the element type.

```ts
console.log(
  Chunk.toReadonlyArray(Chunk.takeWhile(Chunk.make(1, 2, 3, 4, 1), (n) => n < 3))
) // => [1, 2]
```

### drop

Discards the first `n` elements.

```ts
console.log(Chunk.toReadonlyArray(Chunk.drop(Chunk.make(1, 2, 3, 4, 5), 2))) // => [3, 4, 5]
```

### dropRight

Discards the last `n` elements.

```ts
console.log(Chunk.toReadonlyArray(Chunk.dropRight(Chunk.make(1, 2, 3, 4, 5), 2))) // => [1, 2, 3]
```

### dropWhile

Discards the leading run of elements that satisfy a predicate.

```ts
console.log(
  Chunk.toReadonlyArray(Chunk.dropWhile(Chunk.make(1, 2, 3, 4, 5), (n) => n < 3))
) // => [3, 4, 5]
```

### splitAt

Splits into `[before, from]` at an index.

```ts
const [before, after] = Chunk.splitAt(Chunk.make(1, 2, 3, 4), 2)
console.log(Chunk.toReadonlyArray(before)) // => [1, 2]
console.log(Chunk.toReadonlyArray(after))  // => [3, 4]
```

### splitNonEmptyAt

Splits a `NonEmptyChunk` at `n`, returning a non-empty prefix and a possibly-empty
suffix (`n` is clamped to at least `1`).

```ts
const [first, rest] = Chunk.splitNonEmptyAt(Chunk.make(1, 2, 3, 4), 1)
console.log(Chunk.toReadonlyArray(first)) // => [1]
console.log(Chunk.toReadonlyArray(rest))  // => [2, 3, 4]
```

### split

Splits into up to `n` roughly-equal chunks, distributing elements in order.

```ts
console.log(
  Chunk.toReadonlyArray(Chunk.split(Chunk.make(1, 2, 3, 4, 5), 2)).map(Chunk.toReadonlyArray)
) // => [[1, 2, 3], [4, 5]]
```

### splitWhere

Splits on the first element matching a predicate into `[before, fromMatch]`.

```ts
const [before, fromMatch] = Chunk.splitWhere(Chunk.make(1, 2, 3, 4), (n) => n > 2)
console.log(Chunk.toReadonlyArray(before))    // => [1, 2]
console.log(Chunk.toReadonlyArray(fromMatch)) // => [3, 4]
```

### chunksOf

Groups elements into sub-chunks of up to `n` elements each.

```ts
console.log(
  Chunk.toReadonlyArray(Chunk.chunksOf(Chunk.make(1, 2, 3, 4, 5), 2)).map(Chunk.toReadonlyArray)
) // => [[1, 2], [3, 4], [5]]
```

## Searching

### contains

Checks membership using the default `Equal` equivalence.

```ts
console.log(Chunk.contains(Chunk.make(1, 2, 3), 2)) // => true
console.log(Chunk.contains(Chunk.make(1, 2, 3), 9)) // => false
```

### containsWith

Checks membership using a custom equivalence function.

```ts
const ci = Chunk.containsWith<string>((a, b) => a.toLowerCase() === b.toLowerCase())
console.log(ci(Chunk.make("Apple", "Banana"), "apple")) // => true
```

### findFirst

Returns the first element matching a predicate (or refinement) as an `Option`.

```ts
console.log(Chunk.findFirst(Chunk.make(1, 2, 3, 4), (n) => n > 2)) // => Option.some(3)
```

### findFirstIndex

Returns the index of the first matching element as an `Option`.

```ts
console.log(Chunk.findFirstIndex(Chunk.make(1, 2, 3, 4), (n) => n > 2)) // => Option.some(2)
```

### findLast

Returns the last element matching a predicate (or refinement) as an `Option`.

```ts
console.log(Chunk.findLast(Chunk.make(1, 2, 3, 4), (n) => n < 4)) // => Option.some(3)
```

### findLastIndex

Returns the index of the last matching element as an `Option`.

```ts
console.log(Chunk.findLastIndex(Chunk.make(1, 2, 3, 4), (n) => n < 4)) // => Option.some(2)
```

### every

Returns `true` if every element satisfies the predicate; with a refinement it
narrows the chunk's element type.

```ts
console.log(Chunk.every(Chunk.make(2, 4, 6), (n) => n % 2 === 0)) // => true
```

### some

Returns `true` if at least one element satisfies the predicate; narrows the chunk
to `NonEmptyChunk` on success.

```ts
console.log(Chunk.some(Chunk.make(1, 2, 3), (n) => n > 2)) // => true
```

## Modifying

### remove

Deletes the element at an index. Out-of-bounds indices leave the chunk unchanged.

```ts
console.log(Chunk.toReadonlyArray(Chunk.remove(Chunk.make("a", "b", "c"), 1))) // => ["a", "c"]
```

### modify

Applies a function to the element at an index, returning `Option<Chunk>` (`None`
if out of bounds).

```ts
import { Chunk, Option } from "effect"

const result = Chunk.modify(Chunk.make(1, 2, 3), 1, (n) => n * 10)
console.log(Option.map(result, Chunk.toReadonlyArray)) // => Option.some([1, 20, 3])
```

### replace

Replaces the element at an index with a value, returning `Option<Chunk>` (`None`
if out of bounds).

```ts
import { Chunk, Option } from "effect"

const result = Chunk.replace(Chunk.make("a", "b", "c"), 1, "X")
console.log(Option.map(result, Chunk.toReadonlyArray)) // => Option.some(["a", "X", "c"])
```

### reverse

Reverses the order of elements. A `NonEmptyChunk` stays non-empty.

```ts
console.log(Chunk.toReadonlyArray(Chunk.reverse(Chunk.make(1, 2, 3)))) // => [3, 2, 1]
```

## Mapping & folding

### map

Transforms every element. The mapper receives `(element, index)`; non-emptiness
is preserved.

```ts
console.log(Chunk.toReadonlyArray(Chunk.map(Chunk.make(1, 2, 3), (n) => n + 1))) // => [2, 3, 4]
```

### mapAccum

Maps statefully, threading an accumulator and returning `[finalState, chunk]`.

```ts
const [total, running] = Chunk.mapAccum(Chunk.make(1, 2, 3), 0, (acc, n) => [acc + n, acc + n])
console.log(total)                          // => 6
console.log(Chunk.toReadonlyArray(running)) // => [1, 3, 6]
```

### flatMap

Maps each element to a chunk and concatenates the results. The mapper receives
`(element, index)`.

```ts
console.log(
  Chunk.toReadonlyArray(Chunk.flatMap(Chunk.make(1, 2, 3), (n) => Chunk.make(n, n)))
) // => [1, 1, 2, 2, 3, 3]
```

### flatten

Flattens a chunk of chunks into a single chunk.

```ts
console.log(
  Chunk.toReadonlyArray(Chunk.flatten(Chunk.make(Chunk.make(1, 2), Chunk.make(3, 4))))
) // => [1, 2, 3, 4]
```

### forEach

Runs a side-effecting function over each element; returns `void`.

```ts
Chunk.forEach(Chunk.make(1, 2, 3), (n) => console.log(n))
// logs 1, 2, 3
```

### filter

Keeps elements satisfying a predicate; with a refinement it narrows the element
type.

```ts
console.log(
  Chunk.toReadonlyArray(Chunk.filter(Chunk.make(1, 2, 3, 4), (n) => n % 2 === 0))
) // => [2, 4]
```

### filterMap

Maps and filters in one pass: the mapper returns a `Result`, and only `success`
values are kept. The mapper receives `(element, index)`.

```ts
import { Chunk, Result } from "effect"

const nums = Chunk.filterMap(Chunk.make("1", "x", "3"), (s) => {
  const n = parseInt(s)
  return isNaN(n) ? Result.failVoid : Result.succeed(n)
})
console.log(Chunk.toReadonlyArray(nums)) // => [1, 3]
```

### filterMapWhile

Like `filterMap`, but stops at the first element the `Filter` rejects.

```ts
import { Chunk, Result } from "effect"

const result = Chunk.filterMapWhile(Chunk.make("1", "2", "x", "3"), (s) => {
  const n = parseInt(s)
  return isNaN(n) ? Result.failVoid : Result.succeed(n)
})
console.log(Chunk.toReadonlyArray(result)) // => [1, 2]  (stops at "x")
```

### compact

Drops `None`s from a `Chunk<Option<A>>`, keeping the unwrapped `Some` values.

```ts
import { Chunk, Option } from "effect"

console.log(
  Chunk.toReadonlyArray(Chunk.compact(Chunk.make(Option.some(1), Option.none(), Option.some(3))))
) // => [1, 3]
```

### partition

Splits a chunk with a `Result`-returning function into `[excluded, satisfying]`;
the mapper receives `(element, index)`.

```ts
import { Chunk, Result } from "effect"

const [excluded, satisfying] = Chunk.partition(Chunk.make(1, -2, 3), (n) =>
  n > 0 ? Result.succeed(n) : Result.fail(`neg:${n}`)
)
console.log(Chunk.toReadonlyArray(excluded))   // => ["neg:-2"]
console.log(Chunk.toReadonlyArray(satisfying)) // => [1, 3]
```

### separate

Splits a `Chunk<Result<B, A>>` into `[failures, successes]`.

```ts
import { Chunk, Result } from "effect"

const [errors, values] = Chunk.separate(
  Chunk.make(Result.succeed(1), Result.fail("e"), Result.succeed(2))
)
console.log(Chunk.toReadonlyArray(errors)) // => ["e"]
console.log(Chunk.toReadonlyArray(values)) // => [1, 2]
```

### reduce

Folds the elements left-to-right with an accumulator; the reducer receives
`(acc, element, index)`.

```ts
console.log(Chunk.reduce(Chunk.make(1, 2, 3, 4), 0, (acc, n) => acc + n)) // => 10
```

### reduceRight

Folds the elements right-to-left.

```ts
console.log(Chunk.reduceRight(Chunk.make(1, 2, 3, 4), 0, (acc, n) => acc + n)) // => 10
```

### join

Joins a `Chunk<string>` into a single string with a separator.

```ts
console.log(Chunk.join(Chunk.make("a", "b", "c"), ", ")) // => "a, b, c"
```

## Sorting

### sort

Sorts elements with an `Order`, returning a new chunk.

```ts
import { Chunk, Order } from "effect"

console.log(Chunk.toReadonlyArray(Chunk.sort(Chunk.make(3, 1, 2), Order.Number))) // => [1, 2, 3]
```

### sortWith

Sorts by a value derived from each element via a projection plus an `Order`.

```ts
import { Chunk, Order } from "effect"

const people = Chunk.make({ name: "Bob", age: 25 }, { name: "Alice", age: 30 })
const byAge = Chunk.sortWith(people, (p) => p.age, Order.Number)
console.log(Chunk.toReadonlyArray(byAge).map((p) => p.name)) // => ["Bob", "Alice"]
```

## Set operations, zipping & dedupe

### union

Concatenates two chunks and removes duplicates, preserving order of first
appearance.

```ts
console.log(
  Chunk.toReadonlyArray(Chunk.union(Chunk.make(1, 2, 3), Chunk.make(3, 4, 5)))
) // => [1, 2, 3, 4, 5]
```

### intersection

Keeps values present in both chunks; order and references come from the first.

```ts
console.log(
  Chunk.toReadonlyArray(Chunk.intersection(Chunk.make(1, 2, 3, 4), Chunk.make(3, 4, 5)))
) // => [3, 4]
```

### difference

Keeps values from `self` that are not in `that`.

```ts
console.log(
  Chunk.toReadonlyArray(Chunk.difference(Chunk.make(1, 2, 3, 4, 5), Chunk.make(3, 4, 6)))
) // => [1, 2, 5]
```

### differenceWith

Like `difference`, but using a custom equivalence function.

```ts
const byId = Chunk.differenceWith<{ id: number }>((a, b) => a.id === b.id)
const result = byId(Chunk.make({ id: 1 }, { id: 2 }), Chunk.make({ id: 1 }))
console.log(Chunk.toReadonlyArray(result)) // => [{ id: 2 }]
```

### zip

Pairs elements pointwise into tuples, stopping at the shorter chunk.

```ts
console.log(
  Chunk.toReadonlyArray(Chunk.zip(Chunk.make(1, 2, 3), Chunk.make("a", "b", "c")))
) // => [[1, "a"], [2, "b"], [3, "c"]]
```

### zipWith

Combines elements pointwise with a function, stopping at the shorter chunk.

```ts
console.log(
  Chunk.toReadonlyArray(
    Chunk.zipWith(Chunk.make(1, 2, 3), Chunk.make("a", "b", "c"), (n, l) => `${n}-${l}`)
  )
) // => ["1-a", "2-b", "3-c"]
```

### unzip

The inverse of `zip`: splits a chunk of pairs into a pair of chunks.

```ts
const [nums, letters] = Chunk.unzip(Chunk.make([1, "a"] as const, [2, "b"] as const))
console.log(Chunk.toReadonlyArray(nums))    // => [1, 2]
console.log(Chunk.toReadonlyArray(letters)) // => ["a", "b"]
```

### dedupe

Removes duplicate values, keeping the first occurrence of each.

```ts
console.log(Chunk.toReadonlyArray(Chunk.dedupe(Chunk.make(1, 2, 2, 3, 1)))) // => [1, 2, 3]
```

### dedupeAdjacent

Removes only *consecutive* duplicate values, leaving non-adjacent repeats intact.

```ts
console.log(
  Chunk.toReadonlyArray(Chunk.dedupeAdjacent(Chunk.make(1, 1, 2, 2, 3, 1, 1)))
) // => [1, 2, 3, 1]
```