# Building Pipelines

Pipelines compose effects as a sequence of transformations, read left to right.
Where [`Effect.gen`](https://effect.plants.sh/essentials/using-generators/) shines for multi-step
imperative logic, `pipe` shines for short transformations and for attaching
cross-cutting behaviour - logging, spans, retries - to an existing effect. The
two styles interoperate freely; you will use both.

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

// A function to apply a discount, which may fail.
const applyDiscount = (total: number, rate: number) =>
  rate === 0
    ? Effect.fail("Discount rate cannot be zero" as const)
    : Effect.succeed(total - (total * rate) / 100)

const fetchAmount = Effect.succeed(100)

const program = pipe(
  fetchAmount,
  // Log the value without changing it.
  Effect.tap((amount) => Effect.log(`amount: ${amount}`)),
  // Run another effect that depends on the value.
  Effect.flatMap((amount) => applyDiscount(amount, 5)),
  // Transform the success value.
  Effect.map((discounted) => discounted + 1),
  // Replace the value with a formatted string.
  Effect.map((final) => `Final amount to charge: ${final}`)
)

Effect.runFork(program) // logs "amount: 100", produces "Final amount to charge: 96"
```

## `pipe`

`pipe(value, f1, f2, ..., fn)` feeds `value` into `f1`, the result into `f2`,
and so on - the same as `fn(...f2(f1(value)))` but readable top to bottom
instead of inside out. Every effect also has a `.pipe` **method**, so these two
are equivalent:

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

const a = pipe(Effect.succeed(1), Effect.map((n) => n + 1))
const b = Effect.succeed(1).pipe(Effect.map((n) => n + 1))
```

The combinators below are designed for `pipe`: called with their options they
return a function `Effect => Effect`. Each also has a data-first overload
(`Effect.map(self, f)`) if you prefer to pass the effect directly.

## `map` - transform the success value

`Effect.map` applies a plain function to the success value, producing a new
effect. The error and requirement channels are untouched. Effects are immutable;
`map` returns a new effect rather than mutating the original.

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

//      ┌─── Effect<number>
//      ▼
const doubled = Effect.succeed(21).pipe(Effect.map((n) => n * 2))
```

## `as` - replace the success value

`Effect.as(value)` ignores the success value and replaces it with a constant.
`Effect.asVoid` is the common special case that discards the value entirely.

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

const ready = Effect.succeed(5).pipe(Effect.as("ready" as const))
const done = Effect.log("saved").pipe(Effect.asVoid)
```

## `flatMap` - chain a dependent effect

When the next step is itself an effect that depends on the previous value, use
`Effect.flatMap`. It runs the inner effect and flattens the result, so you never
end up with an `Effect<Effect<...>>`. The errors and requirements of both
effects combine in the type.

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

const applyDiscount = (total: number, rate: number) =>
  rate === 0
    ? Effect.fail("Discount rate cannot be zero" as const)
    : Effect.succeed(total - (total * rate) / 100)

//      ┌─── Effect<number, "Discount rate cannot be zero">
//      ▼
const program = Effect.succeed(100).pipe(
  Effect.flatMap((amount) => applyDiscount(amount, 5))
)
```

Make sure every effect you create inside `flatMap` is actually returned or
chained - an effect you build but ignore simply never runs.

## `andThen` - run the next step, value-agnostic

`Effect.andThen` sequences two steps where the second may or may not use the
first's value. The second argument is either an **effect** or a **function
returning an effect**:

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

const fetchAmount = Effect.succeed(100)

// Function returning an effect (like flatMap)...
const a = fetchAmount.pipe(Effect.andThen((amount) => Effect.succeed(amount * 2)))

// ...or a standalone effect to run next, ignoring the previous value.
const b = fetchAmount.pipe(Effect.andThen(Effect.log("fetched")))
```

Use `flatMap` when you specifically want the transformation-of-a-value reading;
use `andThen` when "do this, then do that" is the clearer intent.

## `tap` - run a side effect, keep the value

`Effect.tap` runs an effect for its side effect (logging, metrics, an audit
write) and then passes the **original** value through unchanged. If the tapped
effect fails, the whole chain fails.

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

const applyDiscount = (total: number, rate: number) =>
  Effect.succeed(total - (total * rate) / 100)

const program = Effect.succeed(100).pipe(
  // Observe the amount without consuming it...
  Effect.tap((amount) => Effect.log(`Applying discount to: ${amount}`)),
  // ...`amount` is still available to the next step.
  Effect.flatMap((amount) => applyDiscount(amount, 5))
)
```

`tap` has sibling combinators that fire on the **failure** path -
`tapError`, `tapCause`, `tapDefect` - covered in
[Catching errors](https://effect.plants.sh/essentials/catching-errors/).

## `all` - combine multiple effects

`Effect.all` runs a collection of effects and combines their results, preserving
the shape of the input - a tuple in gives a tuple out, a record in gives a
record out, and any other iterable in gives an array out. By default it runs
sequentially and short-circuits on the first failure.

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

const config = Effect.succeed({ host: "localhost", port: 8080 })
const dbStatus = Effect.succeed("connected")

//      ┌─── Effect<[{ host: string; port: number }, string]>
//      ▼
const startup = Effect.all([config, dbStatus])

// With a record, the keys are preserved:
const named = Effect.all({ config, dbStatus })
//      ▼ Effect<{ config: {...}; dbStatus: string }>
```

`Effect.all` accepts an options object as its second argument:

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

const tasks = [Effect.succeed(1), Effect.succeed(2), Effect.succeed(3)]

// Run all effects concurrently instead of sequentially.
const concurrent = Effect.all(tasks, { concurrency: "unbounded" })
// => Effect<[number, number, number]>

// Discard the results - useful when you only care about side effects.
const discarded = Effect.all(tasks, { discard: true })
// => Effect<void>

// mode: "result" keeps every outcome instead of short-circuiting; each
// element becomes a Result.
const collected = Effect.all(tasks, { mode: "result" })
// => Effect<[Result<number, never>, Result<number, never>, Result<number, never>]>
```

See [Concurrency options](https://effect.plants.sh/concurrency/concurrency-options/) for the full set of
`concurrency` settings.

## Do-notation

Do-notation builds up an **accumulating record** through a pipe, without the
nesting you would get from chained `flatMap`s. Each step adds a named field that
later steps can read. It is the `pipe`-style counterpart to
[`Effect.gen`](https://effect.plants.sh/essentials/using-generators/): prefer `gen` for general
imperative logic, and do-notation when you specifically want to thread a growing
record through a pipeline.

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

const fetchUser = Effect.succeed({ id: 1, name: "Ada" })
const fetchProfile = (userId: number) =>
  Effect.succeed({ userId, bio: "Mathematician" })

const program = Effect.Do.pipe(
  // Bind an effect's value under a name.
  Effect.bind("user", () => fetchUser),
  // Later binds can depend on earlier fields.
  Effect.bind("profile", ({ user }) => fetchProfile(user.id)),
  // `let` adds a plain, synchronous value.
  Effect.let("settings", ({ user }) => ({ theme: "dark", owner: user.name }))
)
// => Effect<{ user: {...}; profile: {...}; settings: {...} }>
// succeeds with:
// {
//   user: { id: 1, name: "Ada" },
//   profile: { userId: 1, bio: "Mathematician" },
//   settings: { theme: "dark", owner: "Ada" }
// }
```

### `Do`

The starting point of a do-notation pipeline: an effect whose success value is
the empty record `{}`, ready for fields to be added.

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

const program = Effect.Do
// => Effect<{}>
```

### `bind`

Runs an effect, possibly depending on the fields accumulated so far, and stores
its success value under a new name. Errors and requirements of the bound effect
combine into the result.

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

const program = Effect.Do.pipe(
  Effect.bind("x", () => Effect.succeed(2)),
  Effect.bind("y", ({ x }) => Effect.succeed(x + 1))
)
// => Effect<{ x: number; y: number }>  (succeeds with { x: 2, y: 3 })
```

### `let`

Adds a computed **plain** value (no effect) to the record, derived from the
fields already present.

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

const program = Effect.Do.pipe(
  Effect.bind("x", () => Effect.succeed(2)),
  Effect.let("doubled", ({ x }) => x * 2)
)
// => Effect<{ x: number; doubled: number }>  (succeeds with { x: 2, doubled: 4 })
```

### `bindTo`

Lifts an existing effect's success value into a one-field record under the given
name - the typical way to **start** a do-notation pipeline from a value you
already have, instead of `Do`.

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

const program = Effect.succeed(5).pipe(
  Effect.bindTo("count"),
  Effect.let("doubled", ({ count }) => count * 2)
)
// => Effect<{ count: number; doubled: number }>  (succeeds with { count: 5, doubled: 10 })
```

## Collection helpers

A handful of combinators on `Effect` apply an effectful function across an
iterable with different error semantics from `forEach` (which short-circuits on
the first failure).

### `partition`

Applies an effectful function to every element and splits the outcomes into
`[excluded, satisfying]` - failures first, successes second. It runs everything
and **never fails**.

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

const program = Effect.partition([0, 1, 2, 3], (n) =>
  n % 2 === 0 ? Effect.fail(`${n} is even`) : Effect.succeed(n)
)
// => Effect<[Array<string>, Array<number>], never>
// succeeds with [ ["0 is even", "2 is even"], [1, 3] ]
```

### `validate`

Runs an effectful function on every element and **accumulates all failures**
instead of stopping at the first - the key difference from `forEach`. If any
element fails, the result fails with a `NonEmptyArray` of every error; otherwise
it succeeds with all the collected values.

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

const allGood = Effect.validate([1, 3, 5], (n) =>
  n % 2 === 0 ? Effect.fail(`${n} is even`) : Effect.succeed(n)
)
// => Effect<Array<number>, NonEmptyArray<string>>  (succeeds with [1, 3, 5])

const someBad = Effect.validate([0, 1, 2, 3], (n) =>
  n % 2 === 0 ? Effect.fail(`${n} is even`) : Effect.succeed(n)
)
// => fails with BOTH errors: ["0 is even", "2 is even"]
// (forEach would have failed with only "0 is even")
```

Pass `{ discard: true }` to validate every element but throw away the successes,
yielding `Effect<void, NonEmptyArray<E>>`:

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

const program = Effect.validate(
  [1, 3],
  (n) => (n % 2 === 0 ? Effect.fail(`${n} is even`) : Effect.succeed(n)),
  { discard: true }
)
// => Effect<void, NonEmptyArray<string>>  (succeeds with undefined)
```

### `findFirst`

Returns the first element whose effectful predicate yields `true`, wrapped in an
`Option`. Short-circuits as soon as a match is found.

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

const program = Effect.findFirst([1, 2, 3, 4], (n) => Effect.succeed(n > 2))
// => Effect<Option<number>>  (succeeds with Option.some(3))
```

### `findFirstFilter`

Like `findFirst`, but the predicate is an effectful **filter** returning a
[`Result`](https://effect.plants.sh/data-types/result/): the first `Result.succeed` both selects and
**transforms** the matching element, returned in `Option.some`.

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

const program = Effect.findFirstFilter([1, 2, 3, 4], (n) =>
  Effect.succeed(n > 2 ? Result.succeed(`found ${n}`) : Result.fail("too small"))
)
// => Effect<Option<string>>  (succeeds with Option.some("found 3"))
```

---

## Reference

A scrollable tour of every sequencing, combining, and transforming combinator
on `Effect`. Common cases are above; this section fills in the rest.

### `map`

Transforms the success value with a plain function, leaving the error and
requirement channels untouched.

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

const program = Effect.succeed(21).pipe(Effect.map((n) => n * 2))
// => Effect<number>  (succeeds with 42)
```

### `mapEager`

Like `map`, but for an **already-resolved** success effect it applies the
function immediately rather than deferring it; pending effects fall back to
regular `map`. A micro-optimisation - reach for plain `map` unless you have a
reason.

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

const resolved = Effect.succeed(5)
const mapped = Effect.mapEager(resolved, (n) => n * 2) // applied eagerly
// => Effect<number>  (succeeds with 10)
```

### `as`

Replaces the success value with a constant, ignoring the original.

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

const program = Effect.succeed(5).pipe(Effect.as("ready" as const))
// => Effect<"ready">
```

### `asSome`

Wraps the success value in `Option.some`, producing an `Effect<Option<A>>`.

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

const program = Effect.succeed(5).pipe(Effect.asSome)
// => Effect<Option<number>>  (succeeds with Option.some(5))
```

### `asVoid`

Discards the success value entirely, producing `Effect<void>`. Common for
fire-and-forget steps whose result you do not need.

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

const program = Effect.succeed(5).pipe(Effect.asVoid)
// => Effect<void>
```

### `flatten`

Collapses a nested `Effect<Effect<A>>` into `Effect<A>`. Handy when a previous
step produced an effect-of-an-effect that you now want to run.

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

const nested = Effect.succeed(Effect.succeed(42))
// => Effect<Effect<number>>

const program = Effect.flatten(nested)
// => Effect<number>  (succeeds with 42)
```

### `flatMap`

Runs an effect that depends on the previous success value and flattens the
result. The error and requirement channels of both effects combine.

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

const program = Effect.succeed(2).pipe(
  Effect.flatMap((n) => Effect.succeed(n + 1))
)
// => Effect<number>  (succeeds with 3)
```

### `flatMapEager`

Like `flatMap`, but when the input effect is already resolved as a success it
chains immediately; pending effects fall back to regular `flatMap`. As with
`mapEager`, an optimisation rather than a default choice.

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

const program = Effect.succeed(2).pipe(
  Effect.flatMapEager((n) => Effect.succeed(n + 1))
)
// => Effect<number>  (succeeds with 3)
```

### `andThen`

Sequences a second step that may or may not use the first's value. Accepts
either an effect or a function returning an effect.

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

const program = Effect.succeed(1).pipe(Effect.andThen(Effect.succeed("done")))
// => Effect<string>  (succeeds with "done")
```

### `tap`

Runs an effect for its side effect and passes the **original** success value
through unchanged. If the tapped effect fails, the chain fails.

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

const program = Effect.succeed(42).pipe(
  Effect.tap((n) => Effect.log(`saw ${n}`))
)
// => Effect<number>  (logs "saw 42", still succeeds with 42)
```

For tapping the failure path, see `tapError`, `tapCause`, and `tapDefect` in
[Catching errors](https://effect.plants.sh/essentials/catching-errors/).

### `zip`

Runs two effects and keeps **both** results as a tuple `[A, B]`. Sequential by
default; pass `{ concurrent: true }` to run them at the same time.

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

const program = Effect.zip(Effect.succeed(1), Effect.succeed("hello"))
// => Effect<[number, string]>  (succeeds with [1, "hello"])

const concurrent = Effect.zip(
  Effect.succeed(1),
  Effect.succeed("hello"),
  { concurrent: true }
)
// => Effect<[number, string]>
```

### `zipWith`

Runs two effects and combines their results with a function, returning a single
value instead of a tuple. Also accepts `{ concurrent: true }`.

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

const program = Effect.zipWith(
  Effect.succeed(1),
  Effect.succeed("hello"),
  (n, s) => n + s.length
)
// => Effect<number>  (succeeds with 6)
```

### `all`

Runs a collection of effects and combines their results, preserving the input
shape (tuple in / tuple out, record in / record out, iterable in / array out).
Short-circuits on the first failure unless `mode: "result"` is set.

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

const program = Effect.all([Effect.succeed(1), Effect.succeed("a")])
// => Effect<[number, string]>  (succeeds with [1, "a"])
```

Options: `{ concurrency }` (see
[Concurrency options](https://effect.plants.sh/concurrency/concurrency-options/)), `{ discard: true }`
to return `Effect<void>`, and `{ mode: "result" }` to collect every outcome
without short-circuiting.

### `forEach`

The iterable counterpart of `all`: applies an effectful function to each element
and collects the results in order. Covered in full on
[Control flow](https://effect.plants.sh/essentials/control-flow/).

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

const program = Effect.forEach([1, 2, 3], (n) => Effect.succeed(n * 2))
// => Effect<Array<number>>  (succeeds with [2, 4, 6])
```

## Cheatsheet

| API           | Input                                       | Output                              |
| ------------- | ------------------------------------------- | ----------------------------------- |
| `map`         | `Effect<A, E, R>`, `A => B`                 | `Effect<B, E, R>`                   |
| `as`          | `Effect<A, E, R>`, `B`                      | `Effect<B, E, R>`                   |
| `asVoid`      | `Effect<A, E, R>`                           | `Effect<void, E, R>`                |
| `flatten`     | `Effect<Effect<A, ...>, ...>`               | `Effect<A, ...>`                    |
| `flatMap`     | `Effect<A, E, R>`, `A => Effect<B, ...>`    | `Effect<B, ...>`                    |
| `andThen`     | `Effect<A, E, R>`, effect or `A => effect`  | `Effect<B, ...>`                    |
| `tap`         | `Effect<A, E, R>`, `A => Effect<X, ...>`    | `Effect<A, ...>`                    |
| `zip`         | `Effect<A, ...>`, `Effect<B, ...>`          | `Effect<[A, B], ...>`               |
| `zipWith`     | two effects + `(A, B) => C`                 | `Effect<C, ...>`                    |
| `all`         | `[Effect<A, ...>, Effect<B, ...>, ...]`     | `Effect<[A, B, ...], ...>`          |
| `Do` / `bind` / `bindTo` | record-building steps            | `Effect<{ ...named fields }, ...>`  |
| `partition`   | `Iterable<A>`, `A => Effect<B, E, R>`       | `Effect<[Array<E>, Array<B>], never, R>` |
| `validate`    | `Iterable<A>`, `A => Effect<B, E, R>`       | `Effect<Array<B>, NonEmptyArray<E>, R>` |
| `findFirst`   | `Iterable<A>`, `A => Effect<boolean, ...>`  | `Effect<Option<A>, ...>`            |

For branching and looping over effects, continue to
[Control flow](https://effect.plants.sh/essentials/control-flow/). To pull values from services inside a
pipeline, see [Accessing services](https://effect.plants.sh/essentials/accessing-services/).