Skip to content

Effect Essentials

An Effect<A, E, R> is a lazy, immutable description of a program. It says: when run, this computation may succeed with a value of type A, fail with an error of type E, and requires a set of services R to be provided. Nothing happens until you hand the description to a runtime (see Runtime) - building effects is pure, running them is the side effect.

This section covers the everyday mechanics of writing that description: the two ways to author effectful code, how to lift existing values and APIs into Effect, and how to sequence and branch between effects.

import { Effect } from "effect"
// A small program: take a number, double it, log it, return it.
const program = Effect.gen(function*() {
const n = yield* Effect.succeed(21)
const doubled = n * 2
yield* Effect.log(`Result: ${doubled}`)
return doubled
})
// `program` is just a value - a description. Running it produces the effect.
Effect.runFork(program)

There are two idiomatic styles, and you will mix them constantly.

  • Effect.gen lets you write effectful code in an imperative, async/await-like style. You yield* an effect to get its success value, and the generator threads errors and services through for you. Reach for it whenever a computation has several sequential steps or local variables.

  • pipe and combinators (Effect.map, Effect.flatMap, Effect.tap, …) compose effects as data transformations. This style shines for short transformations and for attaching cross-cutting behaviour (logging, spans, retries) to an existing effect.

When you write a function that returns an Effect, do not write a plain function that returns Effect.gen(...). Use Effect.fn("name") instead - it gives you the generator syntax plus an automatic tracing span and better stack traces.

  • Using generators - write imperative-style effect code with Effect.gen and yield*.
  • Effect.fn - the right way to define functions that return effects, with named spans.
  • Creating effects - lift plain values, sync code, promises, nullable values, and callbacks into Effect.
  • Building pipelines - compose effects with pipe: map, flatMap, andThen, tap, as.
  • Control flow - conditional and looping combinators: when, forEach, whileLoop, and friends.

Once you are comfortable here, move on to Error Management, Services & Layers, and Runtime.