# Record, Struct & Tuple

Three small, complementary modules cover the everyday shapes of plain JavaScript
data. They all share the same design: **immutable** (every operation returns a
new value, the input is never mutated) and **dual** (call data-first as
`Record.map(record, f)` or data-last in a pipeline as `pipe(record, Record.map(f))`).

| Module   | Use it for                                                          | Example shape                       |
| -------- | ------------------------------------------------------------------- | ----------------------------------- |
| `Record` | Homogeneous string/symbol-keyed maps — collect/map/filter over entries | `Record<string, number>`            |
| `Struct` | Objects with a **known, heterogeneous** shape — pick/omit/evolve while preserving precise types | `{ name: string; age: number }`     |
| `Tuple`  | Fixed-length **positional** data, each slot a different type        | `readonly [number, number, string]` |

Reach for `Record` when keys are dynamic and values share one type. Reach for
`Struct` when the object has a fixed set of named fields you want to manipulate
without losing literal field types. Reach for `Tuple` when position matters and
the length is fixed.

## Record — the common case

`Record` treats a plain object as an immutable dictionary. Lookups that might
miss return an [`Option`](https://effect.plants.sh/data-types/option/); transformations allocate a new
object.

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

const scores = { alice: 1, bob: 2 }

const next = Record.set(scores, "carol", 3)
const doubled = Record.map(next, (score) => score * 2)

console.log(scores) // => { alice: 1, bob: 2 } (unchanged)
console.log(doubled) // => { alice: 2, bob: 4, carol: 6 }
console.log(Record.get(doubled, "alice")) // => Option.some(2)
console.log(Record.get(doubled, "dave")) // => Option.none()
```

## Struct — the common case

`Struct` operates on objects with a fixed shape and **tracks the precise type**
through each step: `pick` narrows to a literal subset, `evolve` may change a
field's type, `renameKeys` updates the key names — all reflected in the result type.

```ts
import { pipe, Struct } from "effect"

const user = { firstName: "Alice", lastName: "Smith", age: 30, admin: false }

const result = pipe(
  user,
  Struct.pick(["firstName", "age"]), // { firstName: string; age: number }
  Struct.evolve({ age: (n) => n + 1 }), // age stays number
  Struct.renameKeys({ firstName: "name" }) // { name: string; age: number }
)

console.log(result) // => { name: "Alice", age: 31 }
```

## Tuple — the common case

`Tuple` works on fixed-length arrays where each position has its own type. The
type system tracks the type at each index, and growth operations like
`appendElement` widen the tuple type.

```ts
import { pipe, Tuple } from "effect"

const point = Tuple.make(10, 20) // [number, number]

const labeled = pipe(
  point,
  Tuple.appendElement("red") // [number, number, string]
)

console.log(labeled) // => [10, 20, "red"]
console.log(Tuple.get(labeled, 2)) // => "red"
```

---

# Record

`ReadonlyRecord<K, A>` is a plain object whose keys are known by type and whose
values share a common type `A`. Traversal APIs (`map`, `keys`, `values`, …) use
`Object.keys`, so they visit enumerable **string** keys only; targeted APIs
(`has`, `get`, `set`, `remove`) also accept symbol keys.

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

## Types

### ReadonlyRecord

The foundational type for immutable string/symbol-keyed mappings.

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

type UserRecord = Record.ReadonlyRecord<"name" | "age", string | number>
// { readonly name: string | number; readonly age: string | number }
```

The `ReadonlyRecord` namespace also exposes type-level helpers:
`ReadonlyRecord.NonLiteralKey<K>` (widens literal keys to `string`/`symbol`) and
`ReadonlyRecord.IntersectKeys<K1, K2>` (computes overlapping keys, used by
`intersection`).

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

type K = Record.ReadonlyRecord.NonLiteralKey<"foo" | "bar"> // string
type I = Record.ReadonlyRecord.IntersectKeys<"a" | "b", "b" | "c"> // "b"
```

### ReadonlyRecordTypeLambda

The higher-kinded type lambda for records, used to plug records into generic
type-constructor machinery (`HKT.Kind`).

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

type Settings = HKT.Kind<
  Record.ReadonlyRecordTypeLambda<"port" | "retries">,
  never,
  never,
  never,
  number
> // Record<"port" | "retries", number>
```

## Constructors

### empty

Creates a new empty record, typed for future operations.

```ts
const r = Record.empty<string, number>()
console.log(r) // => {}
```

### singleton

Creates a record from a single key/value pair.

```ts
console.log(Record.singleton("a", 1)) // => { a: 1 }
```

### fromIterableWith

Builds a record from an iterable, mapping each element to a `[key, value]` tuple.

```ts
console.log(Record.fromIterableWith([1, 2, 3], (a) => [String(a), a * 2]))
// => { "1": 2, "2": 4, "3": 6 }
```

### fromIterableBy

Builds a record from an iterable, deriving each key with the provided function
and storing the element as the value.

```ts
const users = [
  { id: "2", name: "name2" },
  { id: "1", name: "name1" }
]
console.log(Record.fromIterableBy(users, (u) => u.id))
// => { "2": { id: "2", name: "name2" }, "1": { id: "1", name: "name1" } }
```

### fromEntries

Builds a record from an iterable of `[key, value]` pairs (the inverse of
`toEntries`). Later duplicate keys overwrite earlier ones.

```ts
console.log(Record.fromEntries([["a", 1], ["b", 2]])) // => { a: 1, b: 2 }
```

## Conversions

### collect

Transforms record entries into an array using a `(key, value)` mapping function.

```ts
console.log(Record.collect({ a: 1, b: 2, c: 3 }, (key, n) => [key, n]))
// => [["a", 1], ["b", 2], ["c", 3]]
```

### toEntries

Returns the record's entries as an array of `[key, value]` tuples.

```ts
console.log(Record.toEntries({ a: 1, b: 2, c: 3 }))
// => [["a", 1], ["b", 2], ["c", 3]]
```

### keys

Returns the record's string keys as an array.

```ts
console.log(Record.keys({ a: 1, b: 2, c: 3 })) // => ["a", "b", "c"]
```

### values

Returns the record's values as an array.

```ts
console.log(Record.values({ a: 1, b: 2, c: 3 })) // => [1, 2, 3]
```

## Querying

### size

Returns the number of string-keyed entries.

```ts
console.log(Record.size({ a: "a", b: 1, c: true })) // => 3
```

### has

Checks whether a key exists (works with symbol keys too).

```ts
console.log(Record.has({ a: 1, b: 2 }, "a")) // => true
console.log(Record.has({ a: 1, b: 2 }, "c")) // => false
```

### get

Retrieves a value safely as an `Option`.

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

const person: Record<string, unknown> = { name: "John Doe", age: 35 }

console.log(Record.get(person, "name")) // => Option.some("John Doe")
console.log(Record.get(person, "email")) // => Option.none()
```

### isEmptyRecord

Type guard: `true` when a mutable record has no keys.

```ts
console.log(Record.isEmptyRecord({})) // => true
console.log(Record.isEmptyRecord({ a: 3 })) // => false
```

### isEmptyReadonlyRecord

The same guard, typed for `ReadonlyRecord`.

```ts
console.log(Record.isEmptyReadonlyRecord({})) // => true
console.log(Record.isEmptyReadonlyRecord({ a: 3 })) // => false
```

### findFirst

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

```ts
console.log(
  Record.findFirst({ a: 1, b: 2, c: 3 }, (value, key) => value > 1 && key !== "b")
)
// => Option.some(["c", 3])
```

### every

Checks whether every entry satisfies the predicate (also acts as a type guard
when given a refinement).

```ts
console.log(Record.every({ a: 1, b: 2 }, (n) => n > 0)) // => true
console.log(Record.every({ a: 1, b: -1 }, (n) => n > 0)) // => false
```

### some

Checks whether at least one entry satisfies the predicate.

```ts
console.log(Record.some({ a: 1, b: 2 }, (n) => n > 1)) // => true
console.log(Record.some({ a: 1, b: 2 }, (n) => n > 2)) // => false
```

## Modifying

All modifying APIs return new records. The ones that might miss a key return an
`Option`.

### set

Adds or updates a key, returning a new record.

```ts
console.log(Record.set({ a: 1, b: 2 }, "a", 5)) // => { a: 5, b: 2 }
console.log(Record.set({ a: 1, b: 2 }, "c", 5)) // => { a: 1, b: 2, c: 5 }
```

### modify

Applies a function to the value at a key, or returns `Option.none()` if absent.

```ts
const input: Record<string, number> = { a: 3 }
console.log(Record.modify(input, "a", (x) => x * 2)) // => Option.some({ a: 6 })
console.log(Record.modify(input, "b", (x) => x * 2)) // => Option.none()
```

### replace

Replaces the value at an **existing** key; `Option.none()` if the key is absent.

```ts
console.log(Record.replace({ a: 1, b: 2 }, "a", 10)) // => Option.some({ a: 10, b: 2 })
console.log(Record.replace(Record.empty<string>(), "a", 10)) // => Option.none()
```

### remove

Returns a shallow copy without the given key.

```ts
console.log(Record.remove({ a: 1, b: 2 }, "a")) // => { b: 2 }
```

### pop

Removes a key and returns `Option<[value, restOfRecord]>`.

```ts
const input: Record<string, number> = { a: 1, b: 2 }
console.log(Record.pop(input, "a")) // => Option.some([1, { b: 2 }])
console.log(Record.pop(input, "c")) // => Option.none()
```

### map

Maps over the values (the callback also receives the key).

```ts
console.log(Record.map({ a: 3, b: 5 }, (n) => `-${n}`)) // => { a: "-3", b: "-5" }
console.log(Record.map({ a: 3, b: 5 }, (n, key) => `${key}-${n}`))
// => { a: "a-3", b: "b-5" }
```

### mapKeys

Maps over the keys, preserving values.

```ts
console.log(Record.mapKeys({ a: 3, b: 5 }, (key) => key.toUpperCase()))
// => { A: 3, B: 5 }
```

### mapEntries

Maps both keys and values at once, returning a new `[key, value]` per entry.

```ts
console.log(Record.mapEntries({ a: 3, b: 5 }, (a, key) => [key.toUpperCase(), a + 1]))
// => { A: 4, B: 6 }
```

### filterMap

Maps and filters in one pass using [`Result`](https://effect.plants.sh/data-types/result/): `Result.succeed`
keeps the value, `Result.failVoid` drops the entry.

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

const f = (a: number) => (a > 2 ? Result.succeed(a * 2) : Result.failVoid)
console.log(Record.filterMap({ a: 1, b: 2, c: 3 }, f)) // => { c: 6 }
```

### filter

Keeps entries whose value matches the predicate (or refinement).

```ts
console.log(Record.filter({ a: 1, b: 2, c: 3, d: 4 }, (n) => n > 2)) // => { c: 3, d: 4 }
```

### partition

Splits entries into `[failures, successes]` by applying a `Result`-returning
function.

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

const f = (n: number) => (n % 2 === 0 ? Result.succeed(n) : Result.fail(n))
console.log(Record.partition({ a: 1, b: 2, c: 3 }, f)) // => [{ a: 1, c: 3 }, { b: 2 }]
```

### separate

Splits a record of `Result` values into `[failures, successes]`.

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

console.log(Record.separate({ a: Result.fail("e"), b: Result.succeed(1) }))
// => [{ a: "e" }, { b: 1 }]
```

### getSomes

Keeps only the `Some` values from a record of `Option`s.

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

console.log(Record.getSomes({ a: Option.some(1), b: Option.none(), c: Option.some(2) }))
// => { a: 1, c: 2 }
```

### getFailures

Keeps only the failures from a record of `Result`s.

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

console.log(
  Record.getFailures({ a: Result.succeed(1), b: Result.fail("err"), c: Result.succeed(2) })
)
// => { b: "err" }
```

### getSuccesses

Keeps only the successes from a record of `Result`s.

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

console.log(
  Record.getSuccesses({ a: Result.succeed(1), b: Result.fail("err"), c: Result.succeed(2) })
)
// => { a: 1, c: 2 }
```

### reduce

Folds the entries into a single accumulated value.

```ts
console.log(Record.reduce({ a: 1, b: 2, c: 3 }, 0, (acc, value) => acc + value)) // => 6
```

## Combining & comparing

### union

Merges two records, keeping keys from both; overlapping keys are merged with the
combine function.

```ts
console.log(Record.union({ a: 1, b: 2 }, { b: 3, c: 4 }, (a, b) => a + b))
// => { a: 1, b: 5, c: 4 }
```

### intersection

Keeps only keys present in both records, merging their values.

```ts
console.log(Record.intersection({ a: 1, b: 2 }, { b: 3, c: 4 }, (a, b) => a + b))
// => { b: 5 }
```

### difference

Keeps only keys unique to each record; shared keys are dropped.

```ts
console.log(Record.difference({ a: 1, b: 2 }, { b: 3, c: 4 })) // => { a: 1, c: 4 }
```

### isSubrecord

Checks whether every key/value of `self` appears in `that`, comparing values
with Effect equality.

```ts
console.log(Record.isSubrecord({ a: 1 } as Record<string, number>, { a: 1, b: 2 })) // => true
console.log(Record.isSubrecord({ a: 1, b: 2 }, { a: 1 } as Record<string, number>)) // => false
```

### isSubrecordBy

Like `isSubrecord`, but uses a supplied [`Equivalence`](https://effect.plants.sh/traits/equivalence/) to
compare values.

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

const isSub = Record.isSubrecordBy(
  Equivalence.make<string>((a, b) => a.toLowerCase() === b.toLowerCase())
)
console.log(isSub({ role: "Admin" }, { role: "admin", status: "active" })) // => true
```

### makeEquivalence

Builds an `Equivalence` for records from an `Equivalence` for values. Two records
are equivalent when they have the same keys and equivalent values.

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

const eq = Record.makeEquivalence(Equal.asEquivalence<number>())
console.log(eq({ a: 1, b: 2 }, { a: 1, b: 2 })) // => true
console.log(eq({ a: 1, b: 2 }, { a: 1, b: 3 })) // => false
```

### makeReducerUnion

Builds a `Reducer` that folds many records into one with union semantics,
combining overlapping values with the given `Combiner`.

```ts
import { Number as Num, Record } from "effect"

const reducer = Record.makeReducerUnion<string, number>(Num.ReducerSum)
console.log(reducer.combine({ a: 1, b: 2 }, { b: 3, c: 4 })) // => { a: 1, b: 5, c: 4 }
```

### makeReducerIntersection

Builds a `Reducer` whose `combine` intersects two records and combines shared
values. Note: because the reducer's `initialValue` is `{}`, the default
`combineAll` folds from an empty record and yields `{}` for ordinary inputs — use
`combine` directly for pairs.

```ts
import { Number as Num, Record } from "effect"

const reducer = Record.makeReducerIntersection<string, number>(Num.ReducerSum)
console.log(reducer.combine({ a: 1, b: 2 }, { b: 3, c: 4 })) // => { b: 5 }
```

---

# Struct

`Struct` manipulates objects with a fixed, heterogeneous shape, preserving exact
field types through every operation. Functions iterate with `for...in`, so
inherited enumerable properties are included; `keys` returns string keys only.

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

## Utility types

### Simplify

Flattens an intersection like `A & B` into a single readable object type. Purely
cosmetic; preserves `readonly`.

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

type Simplified = Struct.Simplify<{ a: string } & { b: number }>
// { a: string; b: number }
```

### Mutable

Strips `readonly` modifiers (and flattens, like `Simplify`).

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

type Writable = Struct.Mutable<{ readonly a: string; readonly b: number }>
// { a: string; b: number }
```

### Assign

The type-level equivalent of `{ ...T, ...U }`: merges two object types with the
right side winning on overlapping keys.

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

type Merged = Struct.Assign<{ a: string; b: number }, { b: boolean; c: string }>
// { a: string; b: boolean; c: string }
```

## Operations

### get

Extracts a single property; the return type is narrowed to `S[K]`.

```ts
import { pipe, Struct } from "effect"

console.log(pipe({ name: "Alice", age: 30 }, Struct.get("name"))) // => "Alice"
```

### keys

Returns the string keys of a struct, typed as `Array<keyof S & string>`. Symbol
keys are excluded.

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

const user = { name: "Alice", age: 30, [Symbol.for("id")]: 1 }
console.log(Struct.keys(user)) // => ["name", "age"]
```

### pick

Creates a new struct with only the named keys — and **preserves the literal field
types** of those keys.

```ts
import { pipe, Struct } from "effect"

const user = { name: "Alice", age: 30, admin: true }
const result = pipe(user, Struct.pick(["name", "age"]))
// result: { name: string; age: number }
console.log(result) // => { name: "Alice", age: 30 }
```

### omit

The inverse of `pick`: a new struct with the named keys removed, types preserved.

```ts
import { pipe, Struct } from "effect"

const user = { name: "Alice", age: 30, password: "secret" }
console.log(pipe(user, Struct.omit(["password"]))) // => { name: "Alice", age: 30 }
```

### assign

Runtime `{ ...self, ...that }` with proper `Assign` typing — `that` wins on
overlapping keys.

```ts
import { pipe, Struct } from "effect"

const defaults = { theme: "light", lang: "en" }
const overrides = { theme: "dark", fontSize: 14 }
console.log(pipe(defaults, Struct.assign(overrides)))
// => { theme: "dark", lang: "en", fontSize: 14 }
```

### evolve

Transforms selected **values** with per-key functions; untouched keys are copied.
Each function may change the field's type, and the result type tracks that.

```ts
import { pipe, Struct } from "effect"

const result = pipe(
  { name: "alice", age: 30, active: true },
  Struct.evolve({ name: (s) => s.toUpperCase(), age: (n) => n + 1 })
)
console.log(result) // => { name: "ALICE", age: 31, active: true }
```

### evolveKeys

Transforms selected **keys** with per-key functions; values are preserved.

```ts
import { pipe, Struct } from "effect"

console.log(pipe({ name: "Alice", age: 30 }, Struct.evolveKeys({ name: (k) => k.toUpperCase() })))
// => { NAME: "Alice", age: 30 }
```

### evolveEntries

Transforms both keys and values together: each function receives `(key, value)`
and returns `[newKey, newValue]`.

```ts
import { pipe, Struct } from "effect"

const result = pipe(
  { amount: 100, label: "total" },
  Struct.evolveEntries({
    amount: (k, v) => [`${k}Cents`, v * 100],
    label: (k, v) => [k, v.toUpperCase()]
  })
)
console.log(result) // => { amountCents: 10000, label: "TOTAL" }
```

### renameKeys

Declarative key renaming with a static `{ oldKey: newKey }` mapping; unmentioned
keys are copied unchanged.

```ts
import { pipe, Struct } from "effect"

console.log(
  pipe(
    { firstName: "Alice", lastName: "Smith", age: 30 },
    Struct.renameKeys({ firstName: "first", lastName: "last" })
  )
)
// => { first: "Alice", last: "Smith", age: 30 }
```

## Typed mapping (Lambda)

`map`, `mapPick`, and `mapOmit` apply a single transformation to many fields. So
the compiler can track how each field's type changes, the transformation must be
a **`Lambda`** value created with `Struct.lambda` — a plain function will not
type-check.

### Lambda

The type-level function interface. Extend it with concrete `~lambda.in` /
`~lambda.out` types to describe how values are transformed.

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

interface ToString extends Struct.Lambda {
  readonly "~lambda.out": string
}
```

### Apply

Computes the output type a `Lambda` produces for a given input type.

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

interface ToString extends Struct.Lambda {
  readonly "~lambda.out": string
}
type Result = Struct.Apply<ToString, number> // string
```

### lambda

Wraps a plain function as a `Lambda` value for use with `map` / `mapPick` /
`mapOmit`. At runtime it is the same function; only the type changes.

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

interface AsArray extends Struct.Lambda {
  <A>(self: A): Array<A>
  readonly "~lambda.out": Array<this["~lambda.in"]>
}
const asArray = Struct.lambda<AsArray>((a) => [a])
console.log(asArray(1)) // => [1]
```

### map

Applies a `Lambda` to **every** value in the struct.

```ts
import { pipe, Struct } from "effect"

interface AsArray extends Struct.Lambda {
  <A>(self: A): Array<A>
  readonly "~lambda.out": Array<this["~lambda.in"]>
}
const asArray = Struct.lambda<AsArray>((a) => [a])
console.log(pipe({ width: 10, height: 20 }, Struct.map(asArray)))
// => { width: [10], height: [20] }
```

### mapPick

Applies a `Lambda` only to the selected keys; the rest are copied unchanged.

```ts
import { pipe, Struct } from "effect"

interface AsArray extends Struct.Lambda {
  <A>(self: A): Array<A>
  readonly "~lambda.out": Array<this["~lambda.in"]>
}
const asArray = Struct.lambda<AsArray>((a) => [a])
console.log(pipe({ x: 1, y: 2, z: 3 }, Struct.mapPick(["x", "z"], asArray)))
// => { x: [1], y: 2, z: [3] }
```

### mapOmit

Applies a `Lambda` to all keys **except** the selected ones.

```ts
import { pipe, Struct } from "effect"

interface AsArray extends Struct.Lambda {
  <A>(self: A): Array<A>
  readonly "~lambda.out": Array<this["~lambda.in"]>
}
const asArray = Struct.lambda<AsArray>((a) => [a])
console.log(pipe({ x: 1, y: 2, z: 3 }, Struct.mapOmit(["y"], asArray)))
// => { x: [1], y: 2, z: [3] }
```

## Instances

### makeEquivalence

Builds an [`Equivalence`](https://effect.plants.sh/traits/equivalence/) for a struct from a per-field
`Equivalence`. (Alias of `Equivalence.Struct`.)

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

const PersonEq = Struct.makeEquivalence({
  name: Equivalence.strictEqual<string>(),
  age: Equivalence.strictEqual<number>()
})
console.log(PersonEq({ name: "Alice", age: 30 }, { name: "Alice", age: 30 })) // => true
console.log(PersonEq({ name: "Alice", age: 30 }, { name: "Bob", age: 30 })) // => false
```

### makeOrder

Builds an [`Order`](https://effect.plants.sh/traits/order/) for a struct from a per-field `Order`. Fields
are compared in declaration order; the first non-zero result wins. (Alias of
`Order.Struct`.)

```ts
import { Number as Num, String as Str, Struct } from "effect"

const PersonOrder = Struct.makeOrder({ name: Str.Order, age: Num.Order })
console.log(PersonOrder({ name: "Alice", age: 30 }, { name: "Bob", age: 25 })) // => -1
```

### makeCombiner

Builds a `Combiner` for a struct from a per-field
`Combiner`; combining two structs merges each field with its combiner. Pass
`omitKeyWhen` to drop fields whose merged value matches a predicate.

```ts
import { Number as Num, String as Str, Struct } from "effect"

const C = Struct.makeCombiner<{ readonly n: number; readonly s: string }>({
  n: Num.ReducerSum,
  s: Str.ReducerConcat
})
console.log(C.combine({ n: 1, s: "hello" }, { n: 2, s: " world" }))
// => { n: 3, s: "hello world" }
```

### makeReducer

Like `makeCombiner`, but each field's initial value comes from its
`Reducer.initialValue`, so you can fold a whole collection of structs into one.

```ts
import { Number as Num, String as Str, Struct } from "effect"

const R = Struct.makeReducer<{ readonly n: number; readonly s: string }>({
  n: Num.ReducerSum,
  s: Str.ReducerConcat
})
console.log(R.combineAll([{ n: 1, s: "a" }, { n: 2, s: "b" }, { n: 3, s: "c" }]))
// => { n: 6, s: "abc" }
```

### Record

A constructor (re-exported under `Struct`) that builds an object assigning the
same value to each of the given keys.

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

console.log(Struct.Record(["a", "b"], "value")) // => { a: "value", b: "value" }
```

---

# Tuple

`Tuple` works on fixed-length readonly arrays where each position can have a
different type. Element access is by numeric index, and the type system tracks
the type at every slot.

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

### make

Creates a properly typed tuple from its arguments (instead of `[...] as const`).

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

console.log(Tuple.make(10, 20, "red")) // => [10, 20, "red"]
```

### get

Extracts the element at a given index; the index is constrained to valid
positions and the return type tracks the slot's type.

```ts
import { pipe, Tuple } from "effect"

console.log(pipe(Tuple.make(1, true, "hello"), Tuple.get(2))) // => "hello"
```

### pick

Selects elements by index; result order matches the provided indices.

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

console.log(Tuple.pick(["a", "b", "c", "d"], [0, 2, 3])) // => ["a", "c", "d"]
```

### omit

Removes elements by index; the rest keep their original order.

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

console.log(Tuple.omit(["a", "b", "c", "d"], [1, 3])) // => ["a", "c"]
```

### appendElement

Appends one element to the end, **growing the tuple type** to `[...T, E]`.

```ts
import { pipe, Tuple } from "effect"

const result = pipe(Tuple.make(1, 2), Tuple.appendElement("end"))
// result type: [number, number, string]
console.log(result) // => [1, 2, "end"]
```

### appendElements

Concatenates two tuples into `[...T1, ...T2]`, preserving all element types.

```ts
import { pipe, Tuple } from "effect"

console.log(pipe(Tuple.make(1, 2), Tuple.appendElements(["a", "b"] as const)))
// => [1, 2, "a", "b"]
```

### evolve

Transforms elements by position using an array of functions; positions beyond
the array are copied unchanged, and each function may change its element's type.

```ts
import { pipe, Tuple } from "effect"

const result = pipe(
  Tuple.make("hello", 42, true),
  Tuple.evolve([(s) => s.toUpperCase(), (n) => n * 2])
)
console.log(result) // => ["HELLO", 84, true]
```

### renameIndices

Reorders elements by providing an array of **stringified source indices** (e.g.
`["2", "1", "0"]` reverses a 3-tuple), preserving each slot's type.

```ts
import { pipe, Tuple } from "effect"

console.log(pipe(Tuple.make("a", "b", "c"), Tuple.renameIndices(["2", "1", "0"])))
// => ["c", "b", "a"]
```

### map

Applies a `Struct.Lambda` to every element. As with `Struct.map`, the lambda must
be created with `Struct.lambda`.

```ts
import { pipe, Struct, Tuple } from "effect"

interface AsArray extends Struct.Lambda {
  <A>(self: A): Array<A>
  readonly "~lambda.out": Array<this["~lambda.in"]>
}
const asArray = Struct.lambda<AsArray>((a) => [a])
console.log(pipe(Tuple.make(1, "hello", true), Tuple.map(asArray)))
// => [[1], ["hello"], [true]]
```

### mapPick

Applies a `Struct.Lambda` only to the selected indices; other elements are copied.

```ts
import { pipe, Struct, Tuple } from "effect"

interface AsArray extends Struct.Lambda {
  <A>(self: A): Array<A>
  readonly "~lambda.out": Array<this["~lambda.in"]>
}
const asArray = Struct.lambda<AsArray>((a) => [a])
console.log(pipe(Tuple.make(1, "hello", true), Tuple.mapPick([0, 2], asArray)))
// => [[1], "hello", [true]]
```

### mapOmit

Applies a `Struct.Lambda` to all indices **except** the selected ones.

```ts
import { pipe, Struct, Tuple } from "effect"

interface AsArray extends Struct.Lambda {
  <A>(self: A): Array<A>
  readonly "~lambda.out": Array<this["~lambda.in"]>
}
const asArray = Struct.lambda<AsArray>((a) => [a])
console.log(pipe(Tuple.make(1, "hello", true), Tuple.mapOmit([1], asArray)))
// => [[1], "hello", [true]]
```

### makeEquivalence

Builds an [`Equivalence`](https://effect.plants.sh/traits/equivalence/) for tuples from per-position
`Equivalence`s. (Alias of `Equivalence.Tuple`.)

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

const eq = Tuple.makeEquivalence([
  Equivalence.strictEqual<string>(),
  Equivalence.strictEqual<number>()
])
console.log(eq(["Alice", 30], ["Alice", 30])) // => true
console.log(eq(["Alice", 30], ["Bob", 30])) // => false
```

### makeOrder

Builds an [`Order`](https://effect.plants.sh/traits/order/) for tuples from per-position `Order`s,
compared left to right. (Alias of `Order.Tuple`.)

```ts
import { Number as Num, String as Str, Tuple } from "effect"

const ord = Tuple.makeOrder([Str.Order, Num.Order])
console.log(ord(["Alice", 30], ["Bob", 25])) // => -1
console.log(ord(["Alice", 30], ["Alice", 30])) // => 0
```

### makeCombiner

Builds a `Combiner` for a tuple from per-position `Combiner`s; combining merges
each element with its combiner.

```ts
import { Number as Num, String as Str, Tuple } from "effect"

const C = Tuple.makeCombiner<readonly [number, string]>([Num.ReducerSum, Str.ReducerConcat])
console.log(C.combine([1, "hello"], [2, " world"])) // => [3, "hello world"]
```

### makeReducer

Like `makeCombiner`, but derives the initial value from each position's
`Reducer.initialValue`, letting you fold a collection of tuples.

```ts
import { Number as Num, String as Str, Tuple } from "effect"

const R = Tuple.makeReducer<readonly [number, string]>([Num.ReducerSum, Str.ReducerConcat])
console.log(R.combineAll([[1, "a"], [2, "b"], [3, "c"]])) // => [6, "abc"]
```

### isTupleOf

Runtime guard re-exported from `Predicate`: checks an array has exactly `N`
elements, narrowing it to a fixed-length tuple (length only, not element types).

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

const arr: Array<number> = [1, 2, 3]
if (Tuple.isTupleOf(arr, 3)) {
  console.log(arr) // arr: [number, number, number]
}
```

### isTupleOfAtLeast

Runtime guard re-exported from `Predicate`: checks an array has **at least** `N`
elements, narrowing to a tuple with a minimum length.

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

const arr: Array<number> = [1, 2, 3, 4]
if (Tuple.isTupleOfAtLeast(arr, 3)) {
  console.log(arr) // arr: [number, number, number, ...number[]]
}
```