What Does “Effect” Really Mean?
When you first encounter the Effect library, the name itself raises a natural question: what exactly is an “Effect”?
At first glance, it might just look like a wrapper around your code—some abstraction you need to use to map and flatMap. But “Effect” has a much deeper meaning. It’s about capturing the messy reality of side-effects and bringing them under control.
What is a side-effect?
A side-effect is anything a piece of code does in addition to returning a value.
Returning
42has no side-effects.Writing to the console, reading a file, fetching from the network, mutating global state, throwing an error, or generating a random number—all of these are side-effects.
Side-effects aren’t “bad” in themselves. They’re what make programs useful (what good is a program that never touches the outside world?). But side-effects are what make programs hard to reason about. If you don’t track them, they sneak around and bite you with hidden complexity.
From PL theory: effect systems
In Programming Languages (PL) theory, an effect system is like a type system that doesn’t just track what values a function returns, but also what side-effects it may perform.
Here we’re using “side-effects” in the everyday programming sense—things like logging, I/O, randomness, timing, or throwing errors.
(Strictly speaking, PL theory uses the word “effects,” which is a bit more general. For example, it can include nondeterminism or environmental requirements, which aren’t always thought of as “side-effects.” But for most practical purposes, you can think of effects as the familiar side-effects you already know.)
For example:
A pure function might be described as:
number -> numberA parsing function that can throw might look like:
string -> number ! {throws}A function that reads from a file could be:
() -> string ! {io}
That ! {…} part is the effect annotation: it’s telling you what side-effects to expect.
How the Effect library brings this to TypeScript
The Effect library bakes this idea into its core type:
Effect<A, E, R>
Where:
A= the success value typeE= the error type (how it can fail)R= the required environment (dependencies it needs)
This makes every program’s “effectfulness” explicit in its type.
For example:
const pure = Effect.succeed(42)
// Effect<number, never, never>
This has no side-effects at all—it just returns 42.
But:
const program = FileSystem.FileSystem.pipe(
Effect.flatMap(fs => fs.readFileString("data.txt"))
)
// Effect<string, FileSystemError, FileSystem>
This is clearly effectful—it depends on a FileSystem, may fail with a FileSystemError, and returns a string.
The spectrum: from pure to effectful
Here’s the key insight:
An Effect is generalized. It can represent:
A pure computation with no side-effects, like
Effect.succeed(42).A side-effectful computation that logs, reads files, fetches from the network, or sleeps for a second.
Everything lives on the same spectrum, under the same abstraction.
That means you don’t have to switch mental models between “pure code” and “impure code.” You use the same combinators (map, flatMap, pipe) whether you’re adding numbers or orchestrating async I/O.
Describe now, run later
Another crucial aspect: an Effect is not the side-effect itself. It’s a description of a computation that might perform side-effects.
const logHello = Effect.sync(() => console.log("Hello"))
// Effect<void, never, never>
Creating logHello doesn’t print anything yet—it just builds a program. Only when you hand it to the runtime:
await Effect.runPromise(logHello)
does it actually log to the console.
This separation—describe first, run later—is what gives you control. It means you can test, transform, and compose programs safely, and only execute them at the very edge of your app.
So what does “Effect” really mean?
When we say something is an “Effect” in this library, we mean:
It’s not just a value—it’s a program.
It may be pure, or it may perform side-effects like I/O, logging, randomness, or errors.
Either way, it’s tracked in the type system, so you know what you’re dealing with.
It’s composable, so you can build bigger programs out of smaller ones without losing clarity.
An Effect is the unifying abstraction that takes the entire messy world of side-effects and brings it into a form you can reason about, type-check, and compose.
That’s the heart of what “Effect” really means.
References


