Skip to content

Option

Option<A> models a value that may be absent. It is either Some<A>, holding a value of type A, or None, representing no value. Use it instead of null/undefined when absence is a normal, expected outcome — looking up a key, parsing input, reading an optional field. The type then makes the missing case visible and the compiler forces you to handle it.

import { Option } from "effect"
// A partial function: defined only for non-empty arrays
const head = <A>(array: ReadonlyArray<A>): Option.Option<A> =>
array.length > 0 ? Option.some(array[0]) : Option.none()
// Handle both cases explicitly — no `undefined` checks leaking downstream
const describe = <A>(array: ReadonlyArray<A>): string =>
Option.match(head(array), {
onNone: () => "the array is empty",
onSome: (value) => `the first element is ${value}`
})
console.log(describe([1, 2, 3])) // "the first element is 1"
console.log(describe([])) // "the array is empty"

head returns an Option rather than A | undefined, so every caller must decide what to do when the value is missing. Option.match is the safe way to do that: it takes one callback per case and returns a single value.

import { Option } from "effect"
const value = Option.some(1) // { _id: 'Option', _tag: 'Some', value: 1 }
const empty = Option.none() // { _id: 'Option', _tag: 'None' }

Option.none() is a function call (not a constant) so it can infer the element type at the use site.

To build an Option from a predicate, use Option.liftPredicate:

import { Option } from "effect"
// ┌─── (n: number) => Option<number>
// ▼
const parsePositive = Option.liftPredicate((n: number) => n > 0)
console.log(parsePositive(10)) // { _id: 'Option', _tag: 'Some', value: 10 }
console.log(parsePositive(-1)) // { _id: 'Option', _tag: 'None' }

Use Option<A> for properties that may have no value. The key is always present; only the value is optional, which is exactly what Option expresses.

import { Option } from "effect"
interface User {
readonly id: number
readonly username: string
// The `email` key is always present; the value may be absent
readonly email: Option.Option<string>
}
const withEmail: User = {
id: 1,
username: "john_doe",
email: Option.some("john.doe@example.com")
}
const withoutEmail: User = {
id: 2,
username: "jane_doe",
email: Option.none()
}

Option.map transforms the value inside a Some and leaves a None untouched. Option.flatMap is for functions that themselves return an Option, letting you walk nested optional data without manual checks.

import { Option } from "effect"
interface Address {
readonly city: string
readonly street: Option.Option<string>
}
interface User {
readonly address: Option.Option<Address>
}
const user: User = {
address: Option.some({
city: "New York",
street: Option.some("123 Main St")
})
}
// If `address` is None, the chain short-circuits to None.
// Otherwise we dig into the nested optional `street`.
const street = user.address.pipe(
Option.flatMap((address) => address.street)
)
console.log(street) // { _id: 'Option', _tag: 'Some', value: '123 Main St' }

Option.filter keeps the value only if a predicate holds, turning it into None otherwise:

import { Option } from "effect"
// Treat an empty string as "no value"
const nonEmpty = (input: Option.Option<string>) =>
Option.filter(input, (value) => value !== "")
console.log(nonEmpty(Option.some(""))) // { _id: 'Option', _tag: 'None' }
console.log(nonEmpty(Option.some("a"))) // { _id: 'Option', _tag: 'Some', value: 'a' }

To get a plain value back out, supply a fallback so the None case is always handled:

import { Option } from "effect"
// Provide a default for the None case
console.log(Option.getOrElse(Option.some(5), () => 0)) // 5
console.log(Option.getOrElse(Option.none(), () => 0)) // 0
// Interop with code that expects null / undefined
console.log(Option.getOrNull(Option.none())) // null
console.log(Option.getOrUndefined(Option.some(5))) // 5
// Throws if None — use only at boundaries where None is truly impossible
console.log(Option.getOrThrow(Option.some(10))) // 10

Option.orElse tries an alternative when the first Option is None, and Option.firstSomeOf returns the first Some from a list:

import { Option } from "effect"
const fromCache = Option.none<number>()
const fromDb = Option.some(42)
console.log(Option.orElse(fromCache, () => fromDb))
// { _id: 'Option', _tag: 'Some', value: 42 }
console.log(
Option.firstSomeOf([Option.none(), Option.some(2), Option.some(3)])
)
// { _id: 'Option', _tag: 'Some', value: 2 }

Option.fromNullishOr converts null/undefined into None, and any other value into Some. It is the bridge between Effect code and APIs that return nullable values.

import { Option } from "effect"
console.log(Option.fromNullishOr(null)) // { _id: 'Option', _tag: 'None' }
console.log(Option.fromNullishOr(undefined)) // { _id: 'Option', _tag: 'None' }
console.log(Option.fromNullishOr(1)) // { _id: 'Option', _tag: 'Some', value: 1 }

Going the other way, Option.getOrNull / Option.getOrUndefined turn a None back into null / undefined.

Option.all combines several Options into one, preserving the input shape (tuple, struct, or iterable). If any input is None, the result is None.

import { Option } from "effect"
const maybeName: Option.Option<string> = Option.some("John")
const maybeAge: Option.Option<number> = Option.some(25)
// ┌─── Option<{ name: string; age: number }>
// ▼
const struct = Option.all({ name: maybeName, age: maybeAge })
console.log(struct)
// { _id: 'Option', _tag: 'Some', value: { name: 'John', age: 25 } }
// One None makes the whole thing None
console.log(Option.all([Option.some("John"), Option.none()]))
// { _id: 'Option', _tag: 'None' }

To combine two values with a function, use Option.zipWith.

Like Effect.gen, Option.gen lets you write sequential code that short-circuits on the first None. Each yield* either unwraps a Some or aborts the whole block.

import { Option } from "effect"
const maybeName = Option.some("John")
const maybeAge = Option.some(25)
const person = Option.gen(function* () {
const name = (yield* maybeName).toUpperCase()
const age = yield* maybeAge
return { name, age }
})
console.log(person)
// { _id: 'Option', _tag: 'Some', value: { name: 'JOHN', age: 25 } }

If maybeName were None, the generator would stop immediately and return Noneage would never be read. Keep these generators pure: Option is a data structure, not an effect, so avoid side effects inside them.

An Option can be used directly inside Effect.gen. A Some<A> yields its value; a None fails the effect with a NoSuchElementError, which you can then handle with the usual Error Management combinators.

import { Effect, Option } from "effect"
const program = Effect.gen(function* () {
// `yield*` on a Some produces the value;
// a None would fail with NoSuchElementError
const x = yield* Option.some(10)
const y = yield* Option.some(5)
return x + y
})
Effect.runPromise(program).then(console.log) // 15

This makes Option a convenient way to express “may be missing” steps inside an otherwise effectful workflow.

Most combinators are dual: they accept either data-first (Option.map(self, f)) or data-last (self.pipe(Option.map(f))) call styles. The examples below mix both. Items that return a Result cross-reference the Result data type.

Wraps a present value in Some. Does not filter null/undefined.

import { Option } from "effect"
console.log(Option.some(1))
// => { _id: 'Option', _tag: 'Some', value: 1 }

Creates the absent None. It is a function call so the value type infers at the use site (e.g. Option.none<number>()).

import { Option } from "effect"
console.log(Option.none<number>())
// => { _id: 'Option', _tag: 'None' }

Type guard for any Option (either Some or None), useful at runtime boundaries.

import { Option } from "effect"
console.log(Option.isOption(Option.some(1))) // => true
console.log(Option.isOption({})) // => false

Type guard narrowing an Option<A> to Some<A> so you can read .value.

import { Option } from "effect"
const o = Option.some(1)
console.log(Option.isSome(o) ? o.value : "absent") // => 1

Type guard narrowing an Option<A> to None<A>.

import { Option } from "effect"
console.log(Option.isNone(Option.none())) // => true
console.log(Option.isNone(Option.some(1))) // => false

Handles both branches in one expression, returning a plain value.

import { Option } from "effect"
console.log(
Option.match(Option.some(1), {
onNone: () => "empty",
onSome: (value) => `value: ${value}`
})
)
// => "value: 1"

Turns an Option-returning function into a type guard (returns true when the function yields Some). Useful for Array.prototype.filter.

import { Option } from "effect"
const parseString = (x: string | number): Option.Option<string> =>
typeof x === "string" ? Option.some(x) : Option.none()
const isString = Option.toRefinement(parseString)
console.log(isString("a")) // => true
console.log(isString(1)) // => false

Converts a Predicate (or Refinement) into an Option-returning function: Some(value) when it holds, None otherwise.

import { Option } from "effect"
const positive = Option.liftPredicate((n: number) => n > 0)
console.log(positive(1)) // => { _id: 'Option', _tag: 'Some', value: 1 }
console.log(positive(-1)) // => { _id: 'Option', _tag: 'None' }

Wraps the first element of an iterable in Some, or None when empty. Only the first element is consumed.

import { Option } from "effect"
console.log(Option.fromIterable([1, 2, 3])) // => { _id: 'Option', _tag: 'Some', value: 1 }
console.log(Option.fromIterable([])) // => { _id: 'Option', _tag: 'None' }

Treats both null and undefined as None; any other value becomes Some (typed NonNullable<A>).

import { Option } from "effect"
console.log(Option.fromNullishOr(null)) // => { _id: 'Option', _tag: 'None' }
console.log(Option.fromNullishOr(undefined)) // => { _id: 'Option', _tag: 'None' }
console.log(Option.fromNullishOr(1)) // => { _id: 'Option', _tag: 'Some', value: 1 }

Treats only undefined as None, leaving null as a valid Some.

import { Option } from "effect"
console.log(Option.fromUndefinedOr(undefined)) // => { _id: 'Option', _tag: 'None' }
console.log(Option.fromUndefinedOr(null)) // => { _id: 'Option', _tag: 'Some', value: null }

Treats only null as None, leaving undefined as a valid Some.

import { Option } from "effect"
console.log(Option.fromNullOr(null)) // => { _id: 'Option', _tag: 'None' }
console.log(Option.fromNullOr(undefined)) // => { _id: 'Option', _tag: 'Some', value: undefined }

Lifts a function that may return null/undefined into one returning Option (wraps the result via fromNullishOr).

import { Option } from "effect"
const parse = (s: string): number | undefined => {
const n = parseFloat(s)
return isNaN(n) ? undefined : n
}
const parseOption = Option.liftNullishOr(parse)
console.log(parseOption("1")) // => { _id: 'Option', _tag: 'Some', value: 1 }
console.log(parseOption("nope")) // => { _id: 'Option', _tag: 'None' }

Lifts a function that may throw into one returning Option: normal return → Some, thrown exception → None.

import { Option } from "effect"
const parse = Option.liftThrowable(JSON.parse)
console.log(parse("1")) // => { _id: 'Option', _tag: 'Some', value: 1 }
console.log(parse("")) // => { _id: 'Option', _tag: 'None' }

Converts a Result to an Option, keeping the success value and discarding the failure.

import { Option, Result } from "effect"
console.log(Option.getSuccess(Result.succeed("ok"))) // => { _id: 'Option', _tag: 'Some', value: 'ok' }
console.log(Option.getSuccess(Result.fail("err"))) // => { _id: 'Option', _tag: 'None' }

Converts a Result to an Option, keeping the failure value and discarding the success.

import { Option, Result } from "effect"
console.log(Option.getFailure(Result.fail("err"))) // => { _id: 'Option', _tag: 'Some', value: 'err' }
console.log(Option.getFailure(Result.succeed("ok"))) // => { _id: 'Option', _tag: 'None' }

Returns the Some value, or lazily evaluates a fallback thunk on None.

import { Option } from "effect"
console.log(Option.getOrElse(Option.some(1), () => 0)) // => 1
console.log(Option.getOrElse(Option.none(), () => 0)) // => 0

Returns the Some value, or null on None.

import { Option } from "effect"
console.log(Option.getOrNull(Option.some(1))) // => 1
console.log(Option.getOrNull(Option.none())) // => null

Returns the Some value, or undefined on None.

import { Option } from "effect"
console.log(Option.getOrUndefined(Option.some(1))) // => 1
console.log(Option.getOrUndefined(Option.none())) // => undefined

Returns the Some value, or throws a generic Error on None.

import { Option } from "effect"
console.log(Option.getOrThrow(Option.some(1))) // => 1
// Option.getOrThrow(Option.none())
// => throws Error: getOrThrow called on a None

Returns the Some value, or throws the value produced by onNone() on None.

import { Option } from "effect"
console.log(Option.getOrThrowWith(Option.some(1), () => new Error("missing"))) // => 1
// Option.getOrThrowWith(Option.none(), () => new Error("missing"))
// => throws Error: missing

Returns self if Some; otherwise lazily evaluates the fallback Option.

import { Option } from "effect"
console.log(Option.orElse(Option.none(), () => Option.some("b"))) // => { _id: 'Option', _tag: 'Some', value: 'b' }
console.log(Option.orElse(Option.some("a"), () => Option.some("b"))) // => { _id: 'Option', _tag: 'Some', value: 'a' }

Like orElse, but the fallback is a plain value that is automatically wrapped in Some.

import { Option } from "effect"
console.log(Option.orElseSome(Option.none(), () => "b")) // => { _id: 'Option', _tag: 'Some', value: 'b' }

Returns the first available value wrapped in a Result so you can tell whether it came from the primary (Failure) or fallback (Success).

import { Option } from "effect"
console.log(Option.orElseResult(Option.some("primary"), () => Option.some("fallback")))
// => { _id: 'Option', _tag: 'Some', value: { _id: 'Result', _tag: 'Failure', failure: 'primary' } }
console.log(Option.orElseResult(Option.none(), () => Option.some("fallback")))
// => { _id: 'Option', _tag: 'Some', value: { _id: 'Result', _tag: 'Success', success: 'fallback' } }

Returns the first Some in an iterable of Options, or None if all are None. Short-circuits on the first Some.

import { Option } from "effect"
console.log(Option.firstSomeOf([Option.none(), Option.some(1), Option.some(2)]))
// => { _id: 'Option', _tag: 'Some', value: 1 }

Transforms the value inside a Some, leaving None unchanged.

import { Option } from "effect"
console.log(Option.map(Option.some(2), (n) => n * 2)) // => { _id: 'Option', _tag: 'Some', value: 4 }
console.log(Option.map(Option.none<number>(), (n) => n * 2)) // => { _id: 'Option', _tag: 'None' }

Replaces the Some value with a constant, preserving presence/absence.

import { Option } from "effect"
console.log(Option.as(Option.some(42), "x")) // => { _id: 'Option', _tag: 'Some', value: 'x' }

Replaces the Some value with undefined (Option<void>).

import { Option } from "effect"
console.log(Option.asVoid(Option.some(42))) // => { _id: 'Option', _tag: 'Some', value: undefined }

Option.void is a pre-built Some(undefined) constant for the same purpose.

Applies a function returning an Option and flattens the result; None skips the function.

import { Option } from "effect"
console.log(Option.flatMap(Option.some(2), (n) => Option.some(n * 2)))
// => { _id: 'Option', _tag: 'Some', value: 4 }
console.log(Option.flatMap(Option.none<number>(), (n) => Option.some(n)))
// => { _id: 'Option', _tag: 'None' }

Flexible chaining: the next step can be a plain value, an Option, or a function returning either. Plain values are wrapped in Some.

import { Option } from "effect"
console.log(Option.andThen(Option.some(5), (x) => Option.some(x * 2))) // => { _id: 'Option', _tag: 'Some', value: 10 }
console.log(Option.andThen(Option.some(5), "hello")) // => { _id: 'Option', _tag: 'Some', value: 'hello' }

flatMap combined with fromNullishOr: chains a function that may return null/undefined (great for optional-chaining property access).

import { Option } from "effect"
interface Employee { company?: { address?: { street?: { name?: string } } } }
const emp: Employee = { company: { address: { street: { name: "high st" } } } }
console.log(
Option.flatMapNullishOr(Option.some(emp), (e) => e.company?.address?.street?.name)
)
// => { _id: 'Option', _tag: 'Some', value: 'high st' }

Removes one layer of nesting from Option<Option<A>>.

import { Option } from "effect"
console.log(Option.flatten(Option.some(Option.some("v")))) // => { _id: 'Option', _tag: 'Some', value: 'v' }
console.log(Option.flatten(Option.some(Option.none()))) // => { _id: 'Option', _tag: 'None' }

Sequences two Options, keeping the second value when both are Some.

import { Option } from "effect"
console.log(Option.zipRight(Option.some(1), Option.some("hello"))) // => { _id: 'Option', _tag: 'Some', value: 'hello' }
console.log(Option.zipRight(Option.none(), Option.some("hello"))) // => { _id: 'Option', _tag: 'None' }

Sequences two Options, keeping the first value when both are Some.

import { Option } from "effect"
console.log(Option.zipLeft(Option.some("hello"), Option.some(1))) // => { _id: 'Option', _tag: 'Some', value: 'hello' }
console.log(Option.zipLeft(Option.some("hello"), Option.none())) // => { _id: 'Option', _tag: 'None' }

Kleisli composition: combines two Option-returning functions into one, short-circuiting on the first None.

import { Option } from "effect"
const parse = (s: string) => (isNaN(Number(s)) ? Option.none() : Option.some(Number(s)))
const double = (n: number) => (n > 0 ? Option.some(n * 2) : Option.none())
const parseAndDouble = Option.composeK(parse, double)
console.log(parseAndDouble("42")) // => { _id: 'Option', _tag: 'Some', value: 84 }
console.log(parseAndDouble("nope")) // => { _id: 'Option', _tag: 'None' }

Runs a side-condition Option-returning function; keeps the original value if it returns Some, otherwise becomes None.

import { Option } from "effect"
const getInteger = (n: number) => (Number.isInteger(n) ? Option.some(n) : Option.none())
console.log(Option.tap(Option.some(1), getInteger)) // => { _id: 'Option', _tag: 'Some', value: 1 }
console.log(Option.tap(Option.some(1.5), getInteger)) // => { _id: 'Option', _tag: 'None' }

Keeps the value only when a predicate (or refinement) holds; otherwise None.

import { Option } from "effect"
console.log(Option.filter(Option.some("hello"), (s) => s !== "")) // => { _id: 'Option', _tag: 'Some', value: 'hello' }
console.log(Option.filter(Option.some(""), (s) => s !== "")) // => { _id: 'Option', _tag: 'None' }

Transforms and filters at once via a Filter callback that returns a Result: Result.succeed keeps the mapped value, Result.fail discards it.

import { Option, Result } from "effect"
console.log(
Option.filterMap(Option.some(2), (n) =>
n % 2 === 0 ? Result.succeed(`Even: ${n}`) : Result.failVoid
)
)
// => { _id: 'Option', _tag: 'Some', value: 'Even: 2' }

Splits a Some into a [left, right] pair using a function returning a Result: a Failure goes left, a Success goes right.

import { Option, Result } from "effect"
const parse = (s: string) =>
isNaN(Number(s)) ? Result.fail("not a number") : Result.succeed(Number(s))
console.log(Option.partitionMap(Option.some("42"), parse))
// => [ { _id: 'Option', _tag: 'None' }, { _id: 'Option', _tag: 'Some', value: 42 } ]
console.log(Option.partitionMap(Option.some("abc"), parse))
// => [ { _id: 'Option', _tag: 'Some', value: 'not a number' }, { _id: 'Option', _tag: 'None' } ]

Folds an iterable of Options into a single value, skipping every None.

import { Option, pipe } from "effect"
const items = [Option.some(1), Option.none(), Option.some(2)]
console.log(pipe(items, Option.reduceCompact(0, (b, a) => b + a))) // => 3

Converts an Option to an array: [value] for Some, [] for None.

import { Option } from "effect"
console.log(Option.toArray(Option.some(1))) // => [ 1 ]
console.log(Option.toArray(Option.none())) // => []

Combines exactly two Options into Some([a, b]) when both are present.

import { Option } from "effect"
console.log(Option.product(Option.some("hello"), Option.some(42)))
// => { _id: 'Option', _tag: 'Some', value: [ 'hello', 42 ] }
console.log(Option.product(Option.none(), Option.some(42)))
// => { _id: 'Option', _tag: 'None' }

Combines a primary Option with an iterable of same-typed Options into a non-empty tuple; any None yields None.

import { Option } from "effect"
console.log(Option.productMany(Option.some(1), [Option.some(2), Option.some(3)]))
// => { _id: 'Option', _tag: 'Some', value: [ 1, 2, 3 ] }

Combines a tuple, struct, or iterable of Options, preserving the input shape. Any None makes the whole result None.

import { Option } from "effect"
console.log(Option.all([Option.some("John"), Option.some(25)]))
// => { _id: 'Option', _tag: 'Some', value: [ 'John', 25 ] }
console.log(Option.all({ name: Option.some("John"), age: Option.some(25) }))
// => { _id: 'Option', _tag: 'Some', value: { name: 'John', age: 25 } }

Combines two Options using a function; None if either is absent.

import { Option } from "effect"
console.log(Option.zipWith(Option.some(2), Option.some(3), (a, b) => a + b))
// => { _id: 'Option', _tag: 'Some', value: 5 }

Lifts a binary function so it operates on two Options.

import { Option } from "effect"
const add = Option.lift2((a: number, b: number) => a + b)
console.log(add(Option.some(2), Option.some(3))) // => { _id: 'Option', _tag: 'Some', value: 5 }
console.log(add(Option.some(2), Option.none())) // => { _id: 'Option', _tag: 'None' }

Tests whether a Some holds a value equal to the given one, using default structural equality.

import { Option } from "effect"
console.log(Option.contains(Option.some(2), 2)) // => true
console.log(Option.contains(Option.some(1), 2)) // => false
console.log(Option.contains(Option.none(), 2)) // => false

Like contains, but using a custom Equivalence.

import { Equivalence, Option } from "effect"
const check = Option.containsWith(Equivalence.strictEqual<number>())
console.log(check(Option.some(2), 2)) // => true
console.log(check(Option.some(1), 2)) // => false

Tests whether the value in a Some satisfies a predicate (or refinement); None is always false.

import { Option } from "effect"
const isEven = (n: number) => n % 2 === 0
console.log(Option.exists(Option.some(2), isEven)) // => true
console.log(Option.exists(Option.some(1), isEven)) // => false
console.log(Option.exists(Option.none(), isEven)) // => false

Builds an Equivalence for Option<A> from one for A. Two Nones are equal; Some and None differ.

import { Equivalence, Option } from "effect"
const eq = Option.makeEquivalence(Equivalence.strictEqual<number>())
console.log(eq(Option.some(1), Option.some(1))) // => true
console.log(eq(Option.some(1), Option.some(2))) // => false
console.log(eq(Option.none(), Option.none())) // => true

Builds an Order for Option<A> from one for A. None sorts before any Some.

import { Number as N, Option } from "effect"
const ord = Option.makeOrder(N.Order)
console.log(ord(Option.none(), Option.some(1))) // => -1
console.log(ord(Option.some(1), Option.none())) // => 1
console.log(ord(Option.some(1), Option.some(2))) // => -1

The starting point for a do-notation chain: an Option of an empty record.

import { Option, pipe } from "effect"
console.log(
pipe(
Option.Do,
Option.bind("x", () => Option.some(2)),
Option.bind("y", () => Option.some(3))
)
)
// => { _id: 'Option', _tag: 'Some', value: { x: 2, y: 3 } }

Names the value of an existing Option, producing a single-key record — an alternative entry point to Do.

import { Option, pipe } from "effect"
console.log(pipe(Option.some(2), Option.bindTo("x")))
// => { _id: 'Option', _tag: 'Some', value: { x: 2 } }

Adds an Option value to the do-notation record; a None short-circuits the whole chain.

import { Option, pipe } from "effect"
console.log(
pipe(
Option.Do,
Option.bind("x", () => Option.some(2)),
Option.bind("y", () => Option.none<number>())
)
)
// => { _id: 'Option', _tag: 'None' }

Adds a computed plain (non-Option) value to the do-notation record.

import { Option, pipe } from "effect"
console.log(
pipe(
Option.Do,
Option.bind("x", () => Option.some(2)),
Option.let("double", ({ x }) => x * 2)
)
)
// => { _id: 'Option', _tag: 'Some', value: { x: 2, double: 4 } }

Generator-based syntax: each yield* unwraps a Some or short-circuits to None; the return value is wrapped in Some.

import { Option } from "effect"
const person = Option.gen(function* () {
const name = (yield* Option.some("John")).toUpperCase()
const age = yield* Option.some(25)
return { name, age }
})
console.log(person)
// => { _id: 'Option', _tag: 'Some', value: { name: 'JOHN', age: 25 } }

These advanced helpers build Reducer/Combiner instances for aggregating collections of Options.

Builds a Reducer that prioritizes the first non-None value and combines two Somes with the given Combiner (initial value None).

import { Number, Option } from "effect"
const reducer = Option.makeReducer(Number.ReducerSum)
console.log(reducer.combineAll([Option.some(1), Option.none(), Option.some(2)]))
// => { _id: 'Option', _tag: 'Some', value: 3 }

Builds a Combiner with fail-fast semantics: any None operand yields None.

import { Number, Option } from "effect"
const combiner = Option.makeCombinerFailFast(Number.ReducerSum)
console.log(combiner.combine(Option.some(1), Option.some(2))) // => { _id: 'Option', _tag: 'Some', value: 3 }
console.log(combiner.combine(Option.some(1), Option.none())) // => { _id: 'Option', _tag: 'None' }

Lifts an existing Reducer into one over Option with fail-fast semantics (initial value Some(reducer.initialValue)); any None aborts the result.

import { Number, Option } from "effect"
const reducer = Option.makeReducerFailFast(Number.ReducerSum)
console.log(reducer.combineAll([Option.some(1), Option.some(2)])) // => { _id: 'Option', _tag: 'Some', value: 3 }
console.log(reducer.combineAll([Option.some(1), Option.none()])) // => { _id: 'Option', _tag: 'None' }