Result
Result<A, E> represents a computation that either succeeded with a value of
type A or failed with an error of type E. It is a Success<A, E> or a
Failure<A, E>. Use it for synchronous operations that can fail in a known
way — parsing, validation, decoding — when you want the failure in the return
type rather than as a thrown exception.
In Effect v4, Result replaces v3’s Either. The success type comes first
(Result<A, E>, mirroring Effect<A, E, R>), the constructors are
Result.succeed / Result.fail, and the payloads are read via .success and
.failure.
import { Result } from "effect"
// A synchronous parser that reports failure as a value, not an exceptionconst parseAge = (input: string): Result.Result<number, string> => { const n = Number(input) return Number.isInteger(n) && n >= 0 ? Result.succeed(n) : Result.fail(`"${input}" is not a valid age`)}
const message = Result.match(parseAge("42"), { onFailure: (error) => `Invalid: ${error}`, onSuccess: (age) => `Parsed age ${age}`})
console.log(message) // "Parsed age 42"parseAge never throws. Its signature, Result<number, string>, tells callers
both outcomes are possible, and Result.match forces them to handle each.
Creating a Result
Section titled “Creating a Result”import { Result } from "effect"
const ok = Result.succeed(42) // { _id: 'Result', _tag: 'Success', success: 42 }const err = Result.fail("not found") // { _id: 'Result', _tag: 'Failure', failure: 'not found' }Result.fromOption lifts an Option into a Result, supplying the failure to
use for None:
import { Option, Result } from "effect"
const required = Result.fromOption( Option.fromNullishOr(process.env.PORT), () => "PORT is not set")Guards
Section titled “Guards”Result.isSuccess and Result.isFailure narrow the type so you can access the
payload safely:
import { Result } from "effect"
const result: Result.Result<number, string> = Result.succeed(42)
if (Result.isFailure(result)) { console.log(`Failed: ${result.failure}`)} else { console.log(`Succeeded: ${result.success}`)}// "Succeeded: 42"Transforming
Section titled “Transforming”Result.map transforms a success and passes failures through unchanged;
Result.mapError does the opposite. Result.flatMap chains a step that itself
returns a Result, short-circuiting on the first failure.
import { Result } from "effect"
const parse = (s: string): Result.Result<number, string> => Number.isNaN(Number(s)) ? Result.fail(`not a number: ${s}`) : Result.succeed(Number(s))
const positive = (n: number): Result.Result<number, string> => n > 0 ? Result.succeed(n) : Result.fail("must be positive")
// Chain two fallible steps; either failure stops the pipelineconst program = parse("10").pipe( Result.flatMap(positive), Result.map((n) => n * 2))
console.log(program) // { _id: 'Result', _tag: 'Success', success: 20 }Extracting the value
Section titled “Extracting the value”import { Result } from "effect"
// Supply a default for the failure caseconsole.log(Result.getOrElse(Result.fail("oops"), (e) => `recovered from ${e}`))// "recovered from oops"
// Collapse into a single union valueconsole.log(Result.merge(Result.fail("oops"))) // "oops"
// Throw the raw failure — use only where failure is impossibleconsole.log(Result.getOrThrow(Result.succeed(1))) // 1Generator syntax
Section titled “Generator syntax”Result.gen reads like Effect.gen but short-circuits on the first Failure.
Each yield* either unwraps a success or aborts the block with that failure.
import { Result } from "effect"
const parse = (s: string): Result.Result<number, string> => Number.isNaN(Number(s)) ? Result.fail(`not a number: ${s}`) : Result.succeed(Number(s))
const program = Result.gen(function* () { const x = yield* parse("10") const y = yield* parse("5") return x + y})
console.log(program) // { _id: 'Result', _tag: 'Success', success: 15 }Interop with Effect
Section titled “Interop with Effect”A Result can be used directly inside Effect.gen: a Success yields its
value, a Failure fails the effect with E. This makes Result a natural way
to write synchronous, fallible steps inside an effectful workflow without
juggling two error channels.
import { Effect, Result } from "effect"
const parse = (s: string): Result.Result<number, string> => Number.isNaN(Number(s)) ? Result.fail(`not a number: ${s}`) : Result.succeed(Number(s))
const program = Effect.gen(function* () { // A Failure here propagates as an Effect failure of type string const x = yield* parse("10") const y = yield* parse("5") return x + y})
Effect.runPromise(program).then(console.log) // 15Result vs Exit
Section titled “Result vs Exit”Result is for synchronous, expected failures of type E. When you run an
effect, the outcome is an Exit, which wraps a
Cause rather than a bare E — it can also represent defects and
interruptions. Reach for Result in pure code, and for Exit when inspecting
the result of executing an Effect.