EdgeAIG's picture
download
raw
245 kB
import * as Context from "./Context.js";
import * as Duration from "./Duration.js";
import * as Exit from "./Exit.js";
import { constant, dual } from "./Function.js";
import * as core from "./internal/core.js";
import * as internal from "./internal/effect.js";
import * as internalExecutionPlan from "./internal/executionPlan.js";
import * as internalLayer from "./internal/layer.js";
import * as internalRequest from "./internal/request.js";
import * as internalSchedule from "./internal/schedule.js";
import * as Metric from "./Metric.js";
import { CurrentLogAnnotations, CurrentLogSpans } from "./References.js";
import { internalCall } from "./Utils.js";
/**
* Runtime identifier used to recognize `Effect` values.
*
* @category type IDs
* @since 4.0.0
*/
export const TypeId = core.EffectTypeId;
/**
* Checks whether a value is an `Effect`.
*
* **Example** (Checking whether a value is an Effect)
*
* ```ts
* import { Effect } from "effect"
*
* console.log(Effect.isEffect(Effect.succeed(1))) // true
* console.log(Effect.isEffect("hello")) // false
* ```
*
* @category guards
* @since 2.0.0
*/
export const isEffect = core.isEffect;
/**
* Combines an iterable or record of effects into one effect whose success shape
* follows the input.
*
* **When to use**
*
* Use to run a known collection of effects and collect results in the same
* tuple, iterable, or record shape.
*
* **Details**
*
* Tuple and iterable inputs collect results in order. Record inputs collect
* results under the same keys. By default, the combined effect fails on the
* first failure; with concurrent execution, effects that have already started
* may be interrupted, while effects not yet started are skipped.
*
* Options:
*
* Use `concurrency` to control sequential or concurrent execution. Use
* `mode: "result"` to run every effect and collect each success or failure as a
* `Result` in the same output shape. Use `discard: true` to ignore successful
* values and return `void`.
*
* **Example** (Collecting tuple results in order)
*
* ```ts
* import { Console, Effect } from "effect"
*
* const tupleOfEffects = [
* Effect.succeed(42).pipe(Effect.tap(Console.log)),
* Effect.succeed("Hello").pipe(Effect.tap(Console.log))
* ] as const
*
* // ┌─── Effect<[number, string], never, never>
* // ▼
* const resultsAsTuple = Effect.all(tupleOfEffects)
*
* Effect.runPromise(resultsAsTuple).then(console.log)
* // Output:
* // 42
* // Hello
* // [ 42, 'Hello' ]
* ```
*
* **Example** (Collecting iterable results in order)
*
* ```ts
* import { Console, Effect } from "effect"
*
* const iterableOfEffects: Iterable<Effect.Effect<number>> = [1, 2, 3].map(
* (n) => Effect.succeed(n).pipe(Effect.tap(Console.log))
* )
*
* // ┌─── Effect<number[], never, never>
* // ▼
* const resultsAsArray = Effect.all(iterableOfEffects)
*
* Effect.runPromise(resultsAsArray).then(console.log)
* // Output:
* // 1
* // 2
* // 3
* // [ 1, 2, 3 ]
* ```
*
* **Example** (Collecting struct results by key)
*
* ```ts
* import { Console, Effect } from "effect"
*
* const structOfEffects = {
* a: Effect.succeed(42).pipe(Effect.tap(Console.log)),
* b: Effect.succeed("Hello").pipe(Effect.tap(Console.log))
* }
*
* // ┌─── Effect<{ a: number; b: string; }, never, never>
* // ▼
* const resultsAsStruct = Effect.all(structOfEffects)
*
* Effect.runPromise(resultsAsStruct).then(console.log)
* // Output:
* // 42
* // Hello
* // { a: 42, b: 'Hello' }
* ```
*
* **Example** (Collecting record results by key)
*
* ```ts
* import { Console, Effect } from "effect"
*
* const recordOfEffects: Record<string, Effect.Effect<number>> = {
* key1: Effect.succeed(1).pipe(Effect.tap(Console.log)),
* key2: Effect.succeed(2).pipe(Effect.tap(Console.log))
* }
*
* // ┌─── Effect<{ [x: string]: number; }, never, never>
* // ▼
* const resultsAsRecord = Effect.all(recordOfEffects)
*
* Effect.runPromise(resultsAsRecord).then(console.log)
* // Output:
* // 1
* // 2
* // { key1: 1, key2: 2 }
* ```
*
* **Example** (Stopping on the first failure)
*
* ```ts
* import { Console, Effect } from "effect"
*
* const program = Effect.all([
* Effect.succeed("Task1").pipe(Effect.tap(Console.log)),
* Effect.fail("Task2: Oh no!").pipe(Effect.tap(Console.log)),
* // Won't execute due to earlier failure
* Effect.succeed("Task3").pipe(Effect.tap(Console.log))
* ])
*
* Effect.runPromiseExit(program).then(console.log)
* // Output:
* // Task1
* // {
* // _id: 'Exit',
* // _tag: 'Failure',
* // cause: { _id: 'Cause', _tag: 'Fail', failure: 'Task2: Oh no!' }
* // }
* ```
*
* @see {@link forEach} for iterating over elements and applying an effect.
* @category collecting
* @since 2.0.0
*/
export const all = internal.all;
/**
* Applies an effectful function to each element and partitions failures and
* successes.
*
* **Details**
*
* The returned tuple is `[excluded, satisfying]`, where `excluded` contains
* all failures and `satisfying` contains all successes.
*
* This function runs every effect and never fails. Use `concurrency` to control
* parallelism.
*
* **Example** (Separating successes and failures)
*
* ```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.runPromise(program).then(console.log)
* // [ ["0 is even", "2 is even"], [1, 3] ]
* ```
*
* @category collecting
* @since 2.0.0
*/
export const partition = internal.partition;
/**
* Applies an effectful function to each element and accumulates all failures.
*
* **Details**
*
* This function always evaluates every element. If at least one effect fails,
* all failures are returned as a non-empty array and successes are discarded.
* If all effects succeed, it returns all collected successes.
*
* Use `discard: true` to ignore successful values while still validating all
* elements.
*
* **Example** (Validating every element)
*
* ```ts
* import { Effect } from "effect"
*
* const program = Effect.validate([0, 1, 2, 3], (n) =>
* n % 2 === 0 ? Effect.fail(`${n} is even`) : Effect.succeed(n)
* )
*
* Effect.runPromiseExit(program).then(console.log)
* // {
* // _id: 'Exit',
* // _tag: 'Failure',
* // cause: {
* // _id: 'Cause',
* // reasons: [
* // { _id: 'Reason', _tag: 'Fail', error: '0 is even' },
* // { _id: 'Reason', _tag: 'Fail', error: '2 is even' }
* // ]
* // }
* // }
* ```
*
* @category error accumulation
* @since 2.0.0
*/
export const validate = internal.validate;
/**
* Returns the first element that satisfies an effectful predicate.
*
* **Details**
*
* The predicate receives the element and its index. Evaluation short-circuits
* as soon as an element matches.
*
* **Example** (Finding the first successful match)
*
* ```ts
* import { Effect } from "effect"
*
* const program = Effect.findFirst([1, 2, 3, 4], (n) => Effect.succeed(n > 2))
*
* Effect.runPromise(program).then(console.log)
* // { _id: 'Option', _tag: 'Some', value: 3 }
* ```
*
* @category collecting
* @since 2.0.0
*/
export const findFirst = internal.findFirst;
/**
* Returns the first value that passes an effectful `FilterEffect`.
*
* **When to use**
*
* Use when you need to find the first element that satisfies an effectful
* filter returning a `Result`, which also transforms the matching element.
*
* **Details**
*
* The filter receives the element and index. Evaluation short-circuits on the
* first `Result.succeed` and returns the transformed value in `Option.some`.
*
* @see {@link findFirst} for the simpler effectful predicate-based variant
*
* @category collecting
* @since 4.0.0
*/
export const findFirstFilter = internal.findFirstFilter;
/**
* Executes an effectful operation for each element in an `Iterable`.
*
* **When to use**
*
* Use to traverse an iterable with an effectful function while preserving
* element order in the collected results.
*
* **Details**
*
* The `forEach` function applies a provided operation to each element in the
* iterable, producing a new effect that returns an array of results.
*
* If any effect fails, the iteration stops immediately (short-circuiting), and
* the error is propagated.
*
* Concurrency:
*
* The `concurrency` option controls how many operations are performed
* concurrently. By default, the operations are performed sequentially.
*
* Discarding Results:
*
* If the `discard` option is set to `true`, the intermediate results are not
* collected, and the final result of the operation is `void`.
*
* **Example** (Mapping over an iterable with effects)
*
* ```ts
* import { Console, Effect } from "effect"
*
* const result = Effect.forEach(
* [1, 2, 3, 4, 5],
* (n, index) =>
* Console.log(`Currently at index ${index}`).pipe(Effect.as(n * 2))
* )
*
* Effect.runPromise(result).then(console.log)
* // Output:
* // Currently at index 0
* // Currently at index 1
* // Currently at index 2
* // Currently at index 3
* // Currently at index 4
* // [ 2, 4, 6, 8, 10 ]
* ```
*
* **Example** (Running effects without collecting results)
*
* ```ts
* import { Console, Effect } from "effect"
*
* // Apply effects but discard the results
* const result = Effect.forEach(
* [1, 2, 3, 4, 5],
* (n, index) =>
* Console.log(`Currently at index ${index}`).pipe(Effect.as(n * 2)),
* { discard: true }
* )
*
* Effect.runPromise(result).then(console.log)
* // Output:
* // Currently at index 0
* // Currently at index 1
* // Currently at index 2
* // Currently at index 3
* // Currently at index 4
* // undefined
* ```
*
* @see {@link all} for combining multiple effects into one.
* @category collecting
* @since 2.0.0
*/
export const forEach = internal.forEach;
/**
* Executes a body effect repeatedly while a condition holds true.
*
* **Example** (Repeating an effectful loop)
*
* ```ts
* import { Effect } from "effect"
*
* let counter = 0
*
* const program = Effect.whileLoop({
* while: () => counter < 5,
* body: () => Effect.sync(() => ++counter),
* step: (n) => console.log(`Current count: ${n}`)
* })
*
* Effect.runPromise(program)
* // Output:
* // Current count: 1
* // Current count: 2
* // Current count: 3
* // Current count: 4
* // Current count: 5
* ```
*
* @category collecting
* @since 2.0.0
*/
export const whileLoop = internal.whileLoop;
// -----------------------------------------------------------------------------
// Creating Effects
// -----------------------------------------------------------------------------
/**
* Creates an `Effect` that represents an asynchronous computation guaranteed to
* succeed.
*
* **When to use**
*
* Use to convert a `Promise` into an `Effect` when the async operation is
* guaranteed to succeed and will not reject.
*
* **Details**
*
* An optional `AbortSignal` can be provided to allow for interruption of the
* wrapped `Promise` API.
*
* **Gotchas**
*
* The `Promise` must not reject. If it rejects, the rejection is treated as a
* defect, not as a typed failure. Use `tryPromise` when rejection is expected.
*
* Interruption aborts the provided `AbortSignal`, but the underlying
* asynchronous operation only stops if it observes that signal.
*
* **Example** (Wrapping a non-rejecting Promise)
*
* ```ts
* import { Effect } from "effect"
*
* const delay = (message: string) =>
* Effect.promise<string>(
* () =>
* new Promise((resolve) => {
* setTimeout(() => {
* resolve(message)
* }, 2000)
* })
* )
*
* // ┌─── Effect<string, never, never>
* // ▼
* const program = delay("Async operation completed successfully!")
* ```
*
* @see {@link tryPromise} for a version that can handle failures.
* @category constructors
* @since 2.0.0
*/
export const promise = internal.promise;
/**
* Creates an `Effect` that represents an asynchronous computation that might
* fail.
*
* **When to use**
*
* Use when you need to perform asynchronous operations that might fail, such
* as fetching data from an API, and want thrown exceptions or rejected promises
* captured as Effect errors.
*
* **Details**
*
* Error Handling:
*
* There are two ways to handle errors with `tryPromise`:
*
* 1. If you don't provide a `catch` function, the error is caught and the
* effect fails with an `UnknownError`.
* 2. If you provide a `catch` function, the error is caught and the `catch`
* function maps it to an error of type `E`.
*
* Interruptions:
*
* An optional `AbortSignal` can be provided to allow for interruption of the
* wrapped `Promise` API.
*
* **Example** (Wrapping a fetch request that may fail)
*
* ```ts
* import { Effect } from "effect"
*
* const getTodo = (id: number) =>
* // Will catch any errors and propagate them as UnknownError
* Effect.tryPromise(() =>
* fetch(`https://jsonplaceholder.typicode.com/todos/${id}`)
* )
*
* // ┌─── Effect<Response, UnknownError, never>
* // ▼
* const program = getTodo(1)
* ```
*
* **Example** (Mapping Promise rejections to a tagged error)
*
* ```ts
* import { Data, Effect } from "effect"
*
* class TodoFetchError extends Data.TaggedError("TodoFetchError")<{ readonly cause: unknown }> {}
*
* const getTodo = (id: number) =>
* Effect.tryPromise({
* try: () => fetch(`https://jsonplaceholder.typicode.com/todos/${id}`),
* // remap the error
* catch: (cause) => new TodoFetchError({ cause })
* })
*
* // ┌─── Effect<Response, TodoFetchError, never>
* // ▼
* const program = getTodo(1)
* ```
*
* @see {@link promise} if the effectful computation is asynchronous and does not throw errors.
* @category constructors
* @since 2.0.0
*/
export const tryPromise = internal.tryPromise;
/**
* Creates an `Effect` that always succeeds with a given value.
*
* **When to use**
*
* Use when an effect should complete successfully with a specific value without any errors
* or external dependencies.
*
* **Example** (Creating a successful effect)
*
* ```ts
* import { Effect } from "effect"
*
* // Creating an effect that represents a successful scenario
* //
* // ┌─── Effect<number, never, never>
* // ▼
* const success = Effect.succeed(42)
* ```
*
* @see {@link fail} to create an effect that represents a failure.
* @category constructors
* @since 2.0.0
*/
export const succeed = internal.succeed;
/**
* Returns an effect which succeeds with `None`.
*
* **Example** (Succeeding with Option.none)
*
* ```ts
* import { Effect } from "effect"
*
* const program = Effect.succeedNone
*
* Effect.runPromise(program).then(console.log)
* // Output: { _id: 'Option', _tag: 'None' }
* ```
*
* @category constructors
* @since 2.0.0
*/
export const succeedNone = internal.succeedNone;
/**
* Returns an effect which succeeds with the value wrapped in a `Some`.
*
* **Example** (Succeeding with Option.some)
*
* ```ts
* import { Effect } from "effect"
*
* const program = Effect.succeedSome(42)
*
* Effect.runPromise(program).then(console.log)
* // Output: { _id: 'Option', _tag: 'Some', value: 42 }
* ```
*
* @category constructors
* @since 2.0.0
*/
export const succeedSome = internal.succeedSome;
/**
* Creates an `Effect` lazily, delaying construction until it is needed.
*
* **When to use**
*
* Use when you need to defer the evaluation of an effect until it is required.
*
* **Details**
*
* `suspend` takes a thunk that represents an effect and delays creating it
* until the suspended effect is evaluated. This is useful for optimizing
* expensive computations, managing circular dependencies such as recursive
* functions, and helping TypeScript unify return types when branches construct
* different effects. Any side effects or scoped captures inside the thunk are
* re-executed on each invocation.
*
* **Example** (Lazily evaluating side effects)
*
* ```ts
* import { Effect } from "effect"
*
* let i = 0
*
* const bad = Effect.succeed(i++)
*
* const good = Effect.suspend(() => Effect.succeed(i++))
*
* console.log(Effect.runSync(bad)) // Output: 0
* console.log(Effect.runSync(bad)) // Output: 0
*
* console.log(Effect.runSync(good)) // Output: 1
* console.log(Effect.runSync(good)) // Output: 2
* ```
*
* **Example** (Suspending recursive Fibonacci evaluation)
*
* ```ts
* import { Effect } from "effect"
*
* const blowsUp = (n: number): Effect.Effect<number> =>
* n < 2
* ? Effect.succeed(1)
* : Effect.zipWith(blowsUp(n - 1), blowsUp(n - 2), (a, b) => a + b)
*
* // console.log(Effect.runSync(blowsUp(32)))
* // crash: JavaScript heap out of memory
*
* const allGood = (n: number): Effect.Effect<number> =>
* n < 2
* ? Effect.succeed(1)
* : Effect.zipWith(
* Effect.suspend(() => allGood(n - 1)),
* Effect.suspend(() => allGood(n - 2)),
* (a, b) => a + b
* )
*
* console.log(Effect.runSync(allGood(32)))
* // Output: 3524578
* ```
*
* **Example** (Helping TypeScript infer recursive effect types)
*
* ```ts
* import { Effect } from "effect"
*
* // Without suspend, TypeScript may struggle with type inference.
* // Inferred type:
* // (a: number, b: number) =>
* // Effect<never, Error, never> | Effect<number, never, never>
* const withoutSuspend = (a: number, b: number) =>
* b === 0
* ? Effect.fail(new Error("Cannot divide by zero"))
* : Effect.succeed(a / b)
*
* // Using suspend to unify return types.
* // Inferred type:
* // (a: number, b: number) => Effect<number, Error, never>
* const withSuspend = (a: number, b: number) =>
* Effect.suspend(() =>
* b === 0
* ? Effect.fail(new Error("Cannot divide by zero"))
* : Effect.succeed(a / b)
* )
* ```
*
* @category constructors
* @since 2.0.0
*/
export const suspend = internal.suspend;
/**
* Creates an `Effect` that represents a synchronous side-effectful computation.
*
* **When to use**
*
* Use when you need to wrap a synchronous side-effectful operation that is not
* expected to throw.
*
* **Details**
*
* The provided function is evaluated lazily when the effect runs.
*
* **Gotchas**
*
* The function must not throw. If it throws, the thrown value is treated as a
* defect, not as a typed failure. Use `try` when throwing is expected.
*
* **Example** (Capturing synchronous logging in an Effect)
*
* ```ts
* import { Effect } from "effect"
*
* const log = (message: string) =>
* Effect.sync(() => {
* console.log(message) // side effect
* })
*
* // ┌─── Effect<void, never, never>
* // ▼
* const program = log("Hello, World!")
* ```
*
* @see {@link try_ | try} for a version that can handle failures.
* @category constructors
* @since 2.0.0
*/
export const sync = internal.sync;
const void_ = internal.void;
export {
/**
* Returns an effect that succeeds with `void`.
*
* @category constructors
* @since 2.0.0
*/
void_ as void };
const undefined_ = internal.undefined;
export {
/**
* Returns an effect that succeeds with `undefined`.
*
* @category constructors
* @since 4.0.0
*/
undefined_ as undefined };
/**
* Creates an `Effect` from a callback-based asynchronous API.
*
* **When to use**
*
* Use when you need to integrate APIs that complete through callbacks instead
* of returning a `Promise`.
*
* **Details**
*
* The registration function receives a `resume` callback and, when requested,
* an `AbortSignal`. Call `resume` at most once with the effect that should
* complete the fiber; later calls are ignored. Return an optional cleanup
* effect from the registration function to run if the fiber is interrupted.
*
* **Example** (Integrating callback APIs)
*
* ```ts
* import { Effect } from "effect"
*
* const delay = (ms: number) =>
* Effect.callback<void>((resume) => {
* const timeoutId = setTimeout(() => {
* resume(Effect.void)
* }, ms)
* // Cleanup function for interruption
* return Effect.sync(() => clearTimeout(timeoutId))
* })
*
* const program = delay(1000)
* ```
*
* @category constructors
* @since 4.0.0
*/
export const callback = internal.callback;
/**
* Returns an effect that will never produce anything. The moral equivalent of
* `while(true) {}`, only without the wasted CPU cycles.
*
* **Example** (Creating a never-ending effect)
*
* ```ts
* import { Effect } from "effect"
*
* // This effect will never complete
* const program = Effect.never
*
* // This will run forever (or until interrupted)
* // Effect.runPromise(program) // Never resolves
*
* // Use with timeout for practical applications
* const timedProgram = Effect.timeout(program, "1 second")
* ```
*
* @category constructors
* @since 2.0.0
*/
export const never = internal.never;
/**
* Effect that succeeds with an empty record `{}`, used as the starting point
* for do notation chains.
*
* **Example** (Starting do notation)
*
* ```ts
* import { Effect, pipe } from "effect"
*
* const program = pipe(
* Effect.Do,
* Effect.bind("x", () => Effect.succeed(2)),
* Effect.bind("y", ({ x }) => Effect.succeed(x + 1)),
* Effect.let("sum", ({ x, y }) => x + y)
* )
* ```
*
* @category do notation
* @since 2.0.0
*/
export const Do = internal.Do;
/**
* Gives a name to the success value of an `Effect`, creating a single-key
* record used in do notation pipelines.
*
* **When to use**
*
* Use to start a do-notation pipeline from an existing `Effect` when its
* success value should become the first named field in the accumulated record.
*
* @see {@link Do} for starting from an empty accumulated record
* @see {@link bind} for adding fields produced by effects
*
* @category do notation
* @since 2.0.0
*/
export const bindTo = internal.bindTo;
const let_ = internal.let;
export {
/**
* Adds a computed plain value to the do notation record.
*
* **When to use**
*
* Use to add a derived, synchronous value to a do-notation pipeline when it
* depends on fields already accumulated in the record and does not need to run
* another `Effect`.
*
* **Details**
*
* The new field is added with object spreading. If the name already exists in
* the record, the computed value replaces it in the returned type.
*
* @see {@link bind} for adding fields produced by effects
* @see {@link bindTo} for naming an existing success value
* @see {@link Do} for starting from an empty accumulated record
* @see {@link gen} for sequencing without accumulating a record
*
* @category do notation
* @since 2.0.0
*/
let_ as let };
/**
* Adds an `Effect` value to the do notation record under a given name.
*
* **When to use**
*
* Use to sequence an effectful step in a do-notation pipeline when that step
* depends on fields already accumulated in the record and its success value
* should be stored under a name.
*
* **Details**
*
* The function receives the current record, runs the returned effect after the
* input effect succeeds, and inserts its success value under `name`. The
* resulting effect combines the error and service requirements of both steps.
*
* **Gotchas**
*
* Binding a name that already exists replaces that field in the resulting
* record.
*
* @see {@link Do} for starting from an empty do-notation record
* @see {@link bindTo} for naming the success value of an existing effect
* @see {@link gen} for generator-based sequencing without accumulating a record
*
* @category do notation
* @since 2.0.0
*/
export const bind = internal.bind;
/**
* Provides a way to write effectful code using generator functions, simplifying
* control flow and error handling.
*
* **When to use**
*
* Use when you want to write effectful code that looks and behaves like
* synchronous code, while still handling asynchronous tasks, errors, and complex
* control flow such as loops and conditions.
*
* Generator functions work similarly to `async/await` but keep errors,
* requirements, and interruption in the Effect type. You can `yield*` values
* from effects and return the final result at the end.
*
* **Example** (Sequencing effects with generators)
*
* ```ts
* import { Data, Effect } from "effect"
*
* class DiscountRateError extends Data.TaggedError("DiscountRateError")<{}> {}
*
* const addServiceCharge = (amount: number) => amount + 1
*
* const applyDiscount = (
* total: number,
* discountRate: number
* ): Effect.Effect<number, DiscountRateError> =>
* discountRate === 0
* ? Effect.fail(new DiscountRateError())
* : Effect.succeed(total - (total * discountRate) / 100)
*
* const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
*
* const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
*
* export const program = Effect.gen(function*() {
* const transactionAmount = yield* fetchTransactionAmount
* const discountRate = yield* fetchDiscountRate
* const discountedAmount = yield* applyDiscount(
* transactionAmount,
* discountRate
* )
* const finalAmount = addServiceCharge(discountedAmount)
* return `Final amount to charge: ${finalAmount}`
* })
* ```
*
* @category constructors
* @since 2.0.0
*/
export const gen = internal.gen;
/**
* Creates an `Effect` that represents a recoverable error.
*
* **When to use**
*
* Use to explicitly signal a recoverable error in an `Effect`.
*
* **Details**
*
* The error keeps propagating unless it is handled. You can handle tagged
* errors with functions like {@link catchTag} or {@link catchTags}.
*
* **Example** (Creating a failed effect)
*
* ```ts
* import { Data, Effect } from "effect"
*
* class OperationFailedError extends Data.TaggedError("OperationFailedError")<{}> {}
*
* // ┌─── Effect<never, OperationFailedError, never>
* // ▼
* const failure = Effect.fail(
* new OperationFailedError()
* )
* ```
*
* @see {@link succeed} to create an effect that represents a successful value.
* @category constructors
* @since 2.0.0
*/
export const fail = internal.fail;
/**
* Creates an `Effect` that represents a recoverable error using a lazy evaluation.
*
* **When to use**
*
* Use to defer computing a recoverable error value until the effect is run.
*
* **Details**
*
* The error-producing function is evaluated each time the effect is executed.
*
* **Example** (Lazily creating failures)
*
* ```ts
* import { Data, Effect } from "effect"
*
* class ProgramError extends Data.TaggedError("ProgramError")<{ readonly failedAt: Date }> {}
*
* const program = Effect.failSync(() => new ProgramError({ failedAt: new Date() }))
*
* Effect.runPromiseExit(program).then(console.log)
* // Output: { _id: 'Exit', _tag: 'Failure', cause: ... }
* ```
*
* @category constructors
* @since 2.0.0
*/
export const failSync = internal.failSync;
/**
* Creates an `Effect` that represents a failure with a specific `Cause`.
*
* **When to use**
*
* Use when you already have a full `Cause` and need to preserve defects,
* interruptions, annotations, or combined failures in the effect's failure
* channel.
*
* **Details**
*
* This function allows you to create effects that fail with complex error
* structures, including multiple errors, defects, interruptions, and more.
*
* **Example** (Failing with a full Cause)
*
* ```ts
* import { Cause, Effect } from "effect"
*
* const program = Effect.failCause(
* Cause.fail("Network error")
* )
*
* Effect.runPromiseExit(program).then(console.log)
* // Output: { _id: 'Exit', _tag: 'Failure', cause: ... }
* ```
*
* @category constructors
* @since 2.0.0
*/
export const failCause = internal.failCause;
/**
* Creates an `Effect` that represents a failure with a `Cause` computed lazily.
*
* **When to use**
*
* Use to defer computing a full `Cause` until the effect is run.
*
* **Details**
*
* The cause-producing function is evaluated each time the effect is executed.
*
* **Example** (Lazily creating a Cause)
*
* ```ts
* import { Cause, Effect } from "effect"
*
* const program = Effect.failCauseSync(() =>
* Cause.fail("Error computed at runtime")
* )
*
* Effect.runPromiseExit(program).then(console.log)
* // Output: { _id: 'Exit', _tag: 'Failure', cause: ... }
* ```
*
* @category constructors
* @since 2.0.0
*/
export const failCauseSync = internal.failCauseSync;
/**
* Creates an effect that terminates a fiber with a specified error.
*
* **When to use**
*
* Use when you need an `Effect` to report an unrecoverable defect instead of a
* typed error.
*
* **Details**
*
* The `die` function is used to signal a defect, which represents a critical
* and unexpected error in the code. When invoked, it produces an effect that
* does not handle the error and instead terminates the fiber.
*
* The error channel of the resulting effect is of type `never`, indicating that
* it cannot recover from this failure.
*
* **Example** (Failing when division by zero)
*
* ```ts
* import { Effect } from "effect"
*
* const divide = (a: number, b: number) =>
* b === 0
* ? Effect.die(new Error("Cannot divide by zero"))
* : Effect.succeed(a / b)
*
* // ┌─── Effect<number, never, never>
* // ▼
* const program = divide(1, 0)
*
* Effect.runPromise(program).catch(console.error)
* // Output:
* // (FiberFailure) Error: Cannot divide by zero
* // ...stack trace...
* ```
*
* @category constructors
* @since 2.0.0
*/
export const die = internal.die;
const try_ = internal.try;
export {
/**
* Creates an `Effect` that represents a synchronous computation that might
* fail.
*
* **When to use**
*
* Use when you need to perform synchronous operations that might throw, such
* as parsing JSON, and convert thrown exceptions into typed Effect failures.
*
* **Details**
*
* Error Handling:
*
* There are two ways to handle errors with `try`:
*
* 1. If you don't provide a `catch` function, the error is caught and the
* effect fails with an `UnknownError`.
* 2. If you provide a `catch` function, the error is caught and the `catch`
* function maps it to an error of type `E`.
*
* **Example** (Parsing JSON with typed error mapping)
*
* ```ts
* import { Effect } from "effect"
*
* const parseJSON = (input: string) =>
* Effect.try({
* try: () => JSON.parse(input),
* catch: (error) => error as Error
* })
*
* // Success case
* Effect.runPromise(parseJSON("{\"name\": \"Alice\"}")).then(console.log)
* // Output: { name: "Alice" }
*
* // Failure case
* Effect.runPromiseExit(parseJSON("invalid json")).then(console.log)
* // Output: Exit.failure with Error
* ```
*
* **Example** (Mapping synchronous exceptions to a tagged error)
*
* ```ts
* import { Data, Effect } from "effect"
*
* class JsonParsingError extends Data.TaggedError("JsonParsingError")<{ readonly cause: unknown }> {}
*
* const parseJSON = (input: string) =>
* Effect.try({
* try: () => JSON.parse(input),
* catch: (cause) => new JsonParsingError({ cause })
* })
*
* Effect.runPromiseExit(parseJSON("invalid json")).then(console.log)
* // Output: Exit.failure with custom Error message
* ```
*
* @see {@link sync} if the effectful computation is synchronous and does not
* throw errors.
* @category constructors
* @since 2.0.0
*/
try_ as try };
/**
* Yields control back to the Effect runtime, allowing other fibers to execute.
*
* **Example** (Yielding to other fibers)
*
* ```ts
* import { Effect } from "effect"
*
* const program = Effect.gen(function*() {
* console.log("Before yield")
* yield* Effect.yieldNow
* console.log("After yield")
* })
*
* Effect.runPromise(program)
* ```
*
* @category constructors
* @since 2.0.0
*/
export const yieldNow = internal.yieldNow;
/**
* Yields control back to the Effect runtime with a specified priority, allowing other fibers to execute.
*
* **Example** (Yielding with priority)
*
* ```ts
* import { Effect } from "effect"
*
* const program = Effect.gen(function*() {
* console.log("High priority task")
* yield* Effect.yieldNowWith(10) // Higher priority
* console.log("Continued after yield")
* })
*
* Effect.runPromise(program)
* ```
*
* @category constructors
* @since 4.0.0
*/
export const yieldNowWith = internal.yieldNowWith;
/**
* Provides access to the current fiber within an effect computation.
*
* **Example** (Reading the current fiber)
*
* ```ts
* import { Effect } from "effect"
*
* const program = Effect.withFiber((fiber) =>
* Effect.succeed(`Fiber ID: ${fiber.id}`)
* )
*
* Effect.runPromise(program).then(console.log)
* // Output: Fiber ID: 1
* ```
*
* @category constructors
* @since 4.0.0
*/
export const withFiber = core.withFiber;
// -----------------------------------------------------------------------------
// Conversions
// -----------------------------------------------------------------------------
/**
* Converts a `Result` to an `Effect`.
*
* **Example** (Converting a Result into an Effect)
*
* ```ts
* import { Effect, Result } from "effect"
*
* const success = Result.succeed(42)
* const failure = Result.fail("Something went wrong")
*
* const effect1 = Effect.fromResult(success)
* const effect2 = Effect.fromResult(failure)
*
* Effect.runPromise(effect1).then(console.log) // 42
* Effect.runPromiseExit(effect2).then(console.log)
* // { _id: 'Exit', _tag: 'Failure', cause: { _id: 'Cause', _tag: 'Fail', failure: 'Something went wrong' } }
* ```
*
* @category converting
* @since 4.0.0
*/
export const fromResult = internal.fromResult;
/**
* Converts an `Option` into an `Effect`.
*
* **When to use**
*
* Use when absence should become a typed `NoSuchElementError` in the effect error
* channel.
*
* **Details**
*
* `Option.some` becomes a successful effect with the contained value, while
* `Option.none` becomes a failed effect with `NoSuchElementError`.
*
* **Example** (Converting an Option into an Effect)
*
* ```ts
* import { Effect, Option } from "effect"
*
* const some = Option.some(42)
* const none = Option.none()
*
* const effect1 = Effect.fromOption(some)
* const effect2 = Effect.fromOption(none)
*
* Effect.runPromise(effect1).then(console.log) // 42
* Effect.runPromiseExit(effect2).then(console.log)
* // { _id: 'Exit', _tag: 'Failure', cause: { _id: 'Cause', _tag: 'Fail', failure: { _id: 'NoSuchElementError' } } }
* ```
*
* @category converting
* @since 4.0.0
*/
export const fromOption = internal.fromOption;
/**
* Converts a nullable value to an `Effect`, failing with a `NoSuchElementError`
* when the value is `null` or `undefined`.
*
* **Example** (Failing on nullish values)
*
* ```ts
* import { Console, Effect } from "effect"
*
* const program = Effect.fn(function*(input: string | null) {
* const value = yield* Effect.fromNullishOr(input)
* yield* Console.log(value)
* },
* Effect.catch(() => Console.log("missing"))
* )
*
* Effect.runPromise(program(null))
* // Output: missing
* Effect.runPromise(program("hello"))
* // Output: hello
* ```
*
* @category converting
* @since 4.0.0
*/
export const fromNullishOr = internal.fromNullishOr;
// -----------------------------------------------------------------------------
// Mapping
// -----------------------------------------------------------------------------
/**
* Chains effects to produce new `Effect` instances, useful for combining
* operations that depend on previous results.
*
* **When to use**
*
* Use when you need to chain multiple effects, ensuring that each
* step produces a new `Effect` while flattening any nested effects that may
* occur.
*
* **Details**
*
* `flatMap` lets you sequence effects so that the result of one effect can be
* used in the next step. It is similar to `flatMap` used with arrays but works
* specifically with `Effect` instances, allowing you to avoid deeply nested
* effect structures.
*
* Since effects are immutable, `flatMap` always returns a new effect instead of
* changing the original one.
*
* **Example** (Syntax)
*
* ```ts
* import { Effect, pipe } from "effect"
*
* const myEffect = Effect.succeed(1)
* const transformation = (n: number) => Effect.succeed(n + 1)
*
* const flatMappedWithPipe = pipe(myEffect, Effect.flatMap(transformation))
* const flatMappedWithDataFirst = Effect.flatMap(myEffect, transformation)
* const flatMappedWithMethod = myEffect.pipe(Effect.flatMap(transformation))
* ```
*
* **Example** (Sequencing dependent effects)
*
* ```ts
* import { Data, Effect, pipe } from "effect"
*
* class DiscountRateError extends Data.TaggedError("DiscountRateError")<{}> {}
*
* // Function to apply a discount safely to a transaction amount
* const applyDiscount = (
* total: number,
* discountRate: number
* ): Effect.Effect<number, DiscountRateError> =>
* discountRate === 0
* ? Effect.fail(new DiscountRateError())
* : Effect.succeed(total - (total * discountRate) / 100)
*
* // Simulated asynchronous task to fetch a transaction amount from database
* const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
*
* // Chaining the fetch and discount application using `flatMap`
* const finalAmount = pipe(
* fetchTransactionAmount,
* Effect.flatMap((amount) => applyDiscount(amount, 5))
* )
*
* Effect.runPromise(finalAmount).then(console.log)
* // Output: 95
* ```
*
* @see {@link tap} for a version that ignores the result of the effect.
* @category sequencing
* @since 2.0.0
*/
export const flatMap = internal.flatMap;
/**
* Flattens an `Effect` that produces another `Effect` into a single effect.
*
* **Example** (Flattening nested effects)
*
* ```ts
* import { Console, Effect } from "effect"
*
* const nested = Effect.succeed(Effect.succeed("hello"))
*
* const program = Effect.gen(function*() {
* const value = yield* Effect.flatten(nested)
* yield* Console.log(value)
* // Output: hello
* })
* ```
*
* @category sequencing
* @since 2.0.0
*/
export const flatten = internal.flatten;
/**
* Runs this effect and then runs another effect, optionally using the first
* effect's success value to choose the next effect.
*
* **When to use**
*
* Use when you need one effect to run after another and the second effect may
* depend on the first effect's success value.
*
* **Details**
*
* When the second argument is an `Effect`, the first success value is discarded
* and the returned effect produces the second effect's value. When the second
* argument is a function, it receives the first success value and must return
* the next `Effect`.
*
* Failures or requirements from either effect are preserved in the returned
* effect.
*
* **Example** (Syntax)
*
* ```ts
* import { Effect, pipe } from "effect"
*
* const myEffect = Effect.succeed(1)
* const anotherEffect = Effect.succeed("done")
*
* const transformedWithPipe = pipe(myEffect, Effect.andThen(anotherEffect))
* const transformedWithDataFirst = Effect.andThen(myEffect, anotherEffect)
* const transformedWithMethod = myEffect.pipe(Effect.andThen(anotherEffect))
* ```
*
* **Example** (Sequencing a discount calculation after fetching a total)
*
* ```ts
* import { Data, Effect, pipe } from "effect"
*
* class DiscountRateError extends Data.TaggedError("DiscountRateError")<{}> {}
*
* // Function to apply a discount safely to a transaction amount
* const applyDiscount = (
* total: number,
* discountRate: number
* ): Effect.Effect<number, DiscountRateError> =>
* discountRate === 0
* ? Effect.fail(new DiscountRateError())
* : Effect.succeed(total - (total * discountRate) / 100)
*
* // Simulated asynchronous task to fetch a transaction amount from database
* const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
*
* // Using Effect.map and Effect.flatMap
* const result1 = pipe(
* fetchTransactionAmount,
* Effect.map((amount) => amount * 2),
* Effect.flatMap((amount) => applyDiscount(amount, 5))
* )
*
* Effect.runPromise(result1).then(console.log)
* // Output: 190
*
* // Using Effect.andThen
* const result2 = pipe(
* fetchTransactionAmount,
* Effect.andThen((amount) => Effect.succeed(amount * 2)),
* Effect.andThen((amount) => applyDiscount(amount, 5))
* )
*
* Effect.runPromise(result2).then(console.log)
* // Output: 190
* ```
*
* @category sequencing
* @since 2.0.0
*/
export const andThen = internal.andThen;
/**
* Runs a side effect with the result of an effect without changing the original
* value.
*
* **When to use**
*
* Use when you need to run an effectful observation, such as logging or
* tracking, while passing the original success value to the next step.
*
* **Details**
*
* `tap` works similarly to `flatMap`, but it ignores the result of the function
* passed to it. The value from the previous effect remains available for the
* next part of the chain. Note that if the side effect fails, the entire chain
* will fail too.
*
* **Example** (Logging a step in a pipeline)
*
* ```ts
* import { Console, Data, Effect, pipe } from "effect"
*
* class DiscountRateError extends Data.TaggedError("DiscountRateError")<{}> {}
*
* // Function to apply a discount safely to a transaction amount
* const applyDiscount = (
* total: number,
* discountRate: number
* ): Effect.Effect<number, DiscountRateError> =>
* discountRate === 0
* ? Effect.fail(new DiscountRateError())
* : Effect.succeed(total - (total * discountRate) / 100)
*
* // Simulated asynchronous task to fetch a transaction amount from database
* const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
*
* const finalAmount = pipe(
* fetchTransactionAmount,
* // Log the fetched transaction amount
* Effect.tap((amount) => Console.log(`Apply a discount to: ${amount}`)),
* // `amount` is still available!
* Effect.flatMap((amount) => applyDiscount(amount, 5))
* )
*
* Effect.runPromise(finalAmount).then(console.log)
* // Output:
* // Apply a discount to: 100
* // 95
* ```
*
* @category sequencing
* @since 2.0.0
*/
export const tap = internal.tap;
/**
* Converts both success and failure of an `Effect` into a `Result` type.
*
* **When to use**
*
* Use when you want an `Effect`'s typed failures to be handled as `Result`
* data while preserving the original error value.
*
* **Details**
*
* This function converts an effect that may fail into an effect that always
* succeeds, wrapping the outcome in a `Result` type. The result will be
* `Result.Failure` if the effect fails, containing the recoverable error, or
* `Result.Success` if it succeeds, containing the result.
*
* Using this function, you can handle recoverable errors explicitly without
* causing the effect to fail. This is particularly useful in scenarios where
* you want to chain effects and manage both success and failure in the same
* logical flow.
*
* The resulting effect cannot fail directly because all recoverable failures
* are represented inside the `Result` type.
*
* **Gotchas**
*
* `result` only captures typed, recoverable failures. Defects and
* interruptions are not captured inside the `Result` and still fail the
* effect.
*
* **Example** (Capturing success or failure as Result)
*
* ```ts
* import { Effect } from "effect"
*
* const success = Effect.succeed(42)
* const failure = Effect.fail("Something went wrong")
*
* const program1 = Effect.result(success)
* const program2 = Effect.result(failure)
*
* Effect.runPromise(program1).then(console.log)
* // { _id: 'Result', _tag: 'Success', value: 42 }
*
* Effect.runPromise(program2).then(console.log)
* // { _id: 'Result', _tag: 'Failure', error: 'Something went wrong' }
* ```
*
* @see {@link option} for a version that uses `Option` instead.
* @see {@link exit} for a version that encapsulates both recoverable errors and defects in an `Exit`.
*
* @category outcome encapsulation
* @since 4.0.0
*/
export const result = internal.result;
/**
* Converts success to `Option.some` and failure to `Option.none`.
*
* **When to use**
*
* Use when you only care whether an effect succeeds and want recoverable
* failures represented as `Option.none`.
*
* **Details**
*
* Success values become `Option.some`, recoverable failures become
* `Option.none`, and defects still fail the effect.
*
* **Gotchas**
*
* `option` only captures typed, recoverable failures as `Option.none`.
* Defects and interruptions are not captured inside the `Option` and still
* fail the effect.
*
* `option` also discards typed failure values. Use `result` if the failure
* value matters.
*
* **Example** (Capturing success or failure as Option)
*
* ```ts
* import { Console, Effect, Option } from "effect"
*
* const program = Effect.gen(function*() {
* const someValue = yield* Effect.option(Effect.succeed(1))
* const noneValue = yield* Effect.option(Effect.fail("missing"))
*
* yield* Console.log(Option.isSome(someValue))
* yield* Console.log(Option.isNone(noneValue))
* })
*
* Effect.runPromise(program)
* // true
* // true
* ```
*
* @see {@link result} for a version that uses `Result` instead.
* @see {@link exit} for a version that encapsulates both recoverable errors and defects in an `Exit`.
*
* @category outcome encapsulation
* @since 2.0.0
*/
export const option = internal.option;
/**
* Transforms an effect to encapsulate both failure and success using the `Exit`
* data type.
*
* **When to use**
*
* Use when you need to inspect the full outcome, including typed failures, defects,
* and interruptions.
*
* **Details**
*
* `exit` wraps an effect's success or failure inside an `Exit` type, allowing
* you to handle both cases explicitly.
*
* The resulting effect cannot fail because the failure is encapsulated within
* the `Exit.Failure` type. The error type is set to `never`, indicating that
* the effect is structured to never fail directly.
*
* **Example** (Capturing completion as Exit)
*
* ```ts
* import { Effect } from "effect"
*
* const success = Effect.succeed(42)
* const failure = Effect.fail("Something went wrong")
*
* const program1 = Effect.exit(success)
* const program2 = Effect.exit(failure)
*
* Effect.runPromise(program1).then(console.log)
* // { _id: 'Exit', _tag: 'Success', value: 42 }
*
* Effect.runPromise(program2).then(console.log)
* // { _id: 'Exit', _tag: 'Failure', cause: { _id: 'Cause', _tag: 'Fail', failure: 'Something went wrong' } }
* ```
*
* @see {@link option} for a version that uses `Option` instead.
* @see {@link result} for a version that uses `Result` instead.
*
* @category outcome encapsulation
* @since 2.0.0
*/
export const exit = internal.exit;
/**
* Transforms the value inside an effect by applying a function to it.
*
* **When to use**
*
* Use to transform an effect's success value with a function that returns a
* plain value, producing a new effect without changing the original effect's
* typed error or context requirements.
*
* **Details**
*
* `map` takes a function and applies it to the value contained within an
* effect, creating a new effect with the transformed value.
*
* It's important to note that effects are immutable, meaning that the original
* effect is not modified. Instead, a new effect is returned with the updated
* value.
*
* **Example** (Syntax)
*
* ```ts
* import { Effect, pipe } from "effect"
*
* const myEffect = Effect.succeed(1)
* const transformation = (n: number) => n + 1
*
* const mappedWithPipe = pipe(myEffect, Effect.map(transformation))
* const mappedWithDataFirst = Effect.map(myEffect, transformation)
* const mappedWithMethod = myEffect.pipe(Effect.map(transformation))
* ```
*
* **Example** (Adding a service charge)
*
* ```ts
* import { Effect, pipe } from "effect"
*
* const addServiceCharge = (amount: number) => amount + 1
*
* const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
*
* const finalAmount = pipe(
* fetchTransactionAmount,
* Effect.map(addServiceCharge)
* )
*
* Effect.runPromise(finalAmount).then(console.log)
* // Output: 101
* ```
*
* @see {@link mapError} for a version that operates on the error channel.
* @see {@link mapBoth} for a version that operates on both channels.
* @see {@link flatMap} or {@link andThen} for a version that can return a new effect.
* @category mapping
* @since 2.0.0
*/
export const map = internal.map;
/**
* Replaces the value inside an effect with a constant value.
*
* **When to use**
*
* Use to replace a successful value with a constant while preserving failures
* and requirements.
*
* **Details**
*
* `as` allows you to ignore the original value inside an effect and
* replace it with a new constant value.
*
* **Example** (Replacing a success value)
*
* ```ts
* import { Effect, pipe } from "effect"
*
* // Replaces the value 5 with the constant "new value"
* const program = pipe(Effect.succeed(5), Effect.as("new value"))
*
* Effect.runPromise(program).then(console.log)
* // Output: "new value"
* ```
*
* @see {@link map} for deriving the replacement value from the success value
* @see {@link asVoid} for replacing the success value with `void`
*
* @category mapping
* @since 2.0.0
*/
export const as = internal.as;
/**
* Maps the success value of an `Effect` to `Some`, preserving failures.
*
* **Example** (Wrapping success in Option.some)
*
* ```ts
* import { Effect } from "effect"
*
* const program = Effect.asSome(Effect.succeed(42))
*
* Effect.runPromise(program).then(console.log)
* // { _id: 'Option', _tag: 'Some', value: 42 }
* ```
*
* @category mapping
* @since 2.0.0
*/
export const asSome = internal.asSome;
/**
* Maps the success value of an `Effect` to `void`, preserving failures.
*
* **Example** (Discarding success values)
*
* ```ts
* import { Effect } from "effect"
*
* const program = Effect.asVoid(Effect.succeed(42))
*
* Effect.runPromise(program).then(console.log)
* // undefined (void)
* ```
*
* @category mapping
* @since 2.0.0
*/
export const asVoid = internal.asVoid;
/**
* Swaps an effect's success and failure channels.
*
* **When to use**
*
* Use to swap an `Effect`'s success and failure channels.
*
* **Details**
*
* For an `Effect<A, E, R>`, the returned effect has type `Effect<E, A, R>`.
*
* **Example** (Swapping success and failure channels)
*
* ```ts
* import { Effect } from "effect"
*
* // ┌─── Effect<number, string, never>
* // ▼
* const program = Effect.fail("Oh uh!").pipe(Effect.as(2))
*
* // ┌─── Effect<string, number, never>
* // ▼
* const flipped = Effect.flip(program)
* ```
*
* @category mapping
* @since 2.0.0
*/
export const flip = internal.flip;
// -----------------------------------------------------------------------------
// Zipping
// -----------------------------------------------------------------------------
/**
* Combines two effects into a single effect, producing a tuple with the results of both effects.
*
* **When to use**
*
* Use to combine exactly two effects into a tuple.
*
* **Details**
*
* The `zip` function executes the first effect (left) and then the second effect (right).
* Once both effects succeed, their results are combined into a tuple.
*
* Concurrency:
*
* By default, `zip` processes the effects sequentially. To execute the effects concurrently,
* use the `{ concurrent: true }` option.
*
* **Example** (Combining two effects sequentially)
*
* ```ts
* import { Effect } from "effect"
*
* const task1 = Effect.succeed(1).pipe(
* Effect.delay("200 millis"),
* Effect.tap(Effect.log("task1 done"))
* )
* const task2 = Effect.succeed("hello").pipe(
* Effect.delay("100 millis"),
* Effect.tap(Effect.log("task2 done"))
* )
*
* // Combine the two effects together
* //
* // ┌─── Effect<[number, string], never, never>
* // ▼
* const program = Effect.zip(task1, task2)
*
* Effect.runPromise(program).then(console.log)
* // Output:
* // timestamp=... level=INFO fiber=#0 message="task1 done"
* // timestamp=... level=INFO fiber=#0 message="task2 done"
* // [ 1, 'hello' ]
* ```
*
* **Example** (Combining two effects concurrently)
*
* ```ts
* import { Effect } from "effect"
*
* const task1 = Effect.succeed(1).pipe(
* Effect.delay("200 millis"),
* Effect.tap(Effect.log("task1 done"))
* )
* const task2 = Effect.succeed("hello").pipe(
* Effect.delay("100 millis"),
* Effect.tap(Effect.log("task2 done"))
* )
*
* // Run both effects concurrently using the concurrent option
* const program = Effect.zip(task1, task2, { concurrent: true })
*
* Effect.runPromise(program).then(console.log)
* // Output:
* // timestamp=... level=INFO fiber=#0 message="task2 done"
* // timestamp=... level=INFO fiber=#0 message="task1 done"
* // [ 1, 'hello' ]
* ```
*
* @see {@link zipWith} for a version that combines the results with a custom function.
* @see {@link all} for collecting a larger structure of effects.
*
* @category zipping
* @since 2.0.0
*/
export const zip = internal.zip;
/**
* Combines two effects sequentially and applies a function to their results to
* produce a single value.
*
* **When to use**
*
* Use when you need to run two effects sequentially and combine their results
* with a function instead of keeping the results as a tuple.
*
* **Details**
*
* Concurrency:
*
* By default, the effects are run sequentially. To execute them concurrently,
* use the `{ concurrent: true }` option.
*
* **Example** (Combining two success values with a function)
*
* ```ts
* import { Effect } from "effect"
*
* const task1 = Effect.succeed(1).pipe(
* Effect.delay("200 millis"),
* Effect.tap(Effect.log("task1 done"))
* )
* const task2 = Effect.succeed("hello").pipe(
* Effect.delay("100 millis"),
* Effect.tap(Effect.log("task2 done"))
* )
*
* const task3 = Effect.zipWith(
* task1,
* task2,
* // Combines results into a single value
* (number, string) => number + string.length
* )
*
* Effect.runPromise(task3).then(console.log)
* // Output:
* // timestamp=... level=INFO fiber=#3 message="task1 done"
* // timestamp=... level=INFO fiber=#2 message="task2 done"
* // 6
* ```
*
* @category zipping
* @since 2.0.0
*/
export const zipWith = internal.zipWith;
// -----------------------------------------------------------------------------
// Error handling
// -----------------------------------------------------------------------------
const catch_ = internal.catch_;
export {
/**
* Handles all errors in an effect by providing a fallback effect.
*
* **When to use**
*
* Use when every recoverable error from an effect should be handled by the same
* fallback function while unrecoverable defects remain defects.
*
* **Details**
*
* The `catch` function catches any errors that may occur during the
* execution of an effect and allows you to handle them by specifying a fallback
* effect. This ensures that the program continues without failing by recovering
* from errors using the provided fallback logic.
*
* **Gotchas**
*
* `catch` only handles recoverable errors. It will not recover from
* unrecoverable defects.
*
* @see {@link catchCause} for a version that can recover from both recoverable and unrecoverable errors.
*
* @category error handling
* @since 4.0.0
*/
catch_ as catch };
/**
* Catches and handles specific errors by their `_tag` field, which is used as a
* discriminator.
*
* **When to use**
*
* Use when you need to recover from one specific tagged error in an effect
* error channel.
*
* **Details**
*
* The error type must have a readonly `_tag` field. `catchTag` matches that
* field and only handles errors with the requested tag.
*
* **Example** (Handling a tagged error)
*
* ```ts
* import { Effect } from "effect"
*
* class NetworkError {
* readonly _tag = "NetworkError"
* constructor(readonly message: string) {}
* }
*
* class ValidationError {
* readonly _tag = "ValidationError"
* constructor(readonly message: string) {}
* }
*
* declare const task: Effect.Effect<string, NetworkError | ValidationError>
*
* const program = Effect.catchTag(
* task,
* "NetworkError",
* (error) => Effect.succeed(`Recovered from network error: ${error.message}`)
* )
* ```
*
* @see {@link catchTags} for handling multiple tagged errors in one call
* @see {@link catchIf} for recovering from errors that match a predicate
*
* @category error handling
* @since 2.0.0
*/
export const catchTag = internal.catchTag;
/**
* Handles multiple errors in a single block of code using their `_tag` field.
*
* **When to use**
*
* Use when one recovery step should handle several tagged error types by
* matching their readonly `_tag` fields.
*
* **Details**
*
* Pass a handler table whose keys are tags, plus an optional fallback for
* unmatched errors.
*
* The error type must have a readonly `_tag` field to use `catchTags`. This
* field is used to identify and match errors.
*
* **Example** (Handling multiple tagged errors)
*
* ```ts
* import { Data, Effect } from "effect"
*
* // Define tagged error types
* class ValidationError extends Data.TaggedError("ValidationError")<{
* message: string
* }> {}
*
* class NetworkError extends Data.TaggedError("NetworkError")<{
* statusCode: number
* }> {}
*
* // An effect that might fail with multiple error types
* declare const program: Effect.Effect<string, ValidationError | NetworkError>
*
* // Handle multiple error types at once
* const handled = Effect.catchTags(program, {
* ValidationError: (error) =>
* Effect.succeed(`Validation failed: ${error.message}`),
* NetworkError: (error) => Effect.succeed(`Network error: ${error.statusCode}`)
* })
* ```
*
* @category error handling
* @since 2.0.0
*/
export const catchTags = internal.catchTags;
/**
* Catches a specific reason within a tagged error.
*
* **When to use**
*
* Use to handle one nested reason inside an `Effect`'s tagged error while
* preserving the parent error shape for unmatched reasons.
*
* **Details**
*
* Use this to handle nested error causes without removing the parent error
* from the error channel. The handler receives the unwrapped reason.
*
* **Example** (Handling an error reason)
*
* ```ts
* import { Data, Effect } from "effect"
*
* class RateLimitError extends Data.TaggedError("RateLimitError")<{
* retryAfter: number
* }> {}
*
* class QuotaExceededError extends Data.TaggedError("QuotaExceededError")<{
* limit: number
* }> {}
*
* class AiError extends Data.TaggedError("AiError")<{
* reason: RateLimitError | QuotaExceededError
* }> {}
*
* declare const program: Effect.Effect<string, AiError>
*
* // Handle rate limits specifically
* const handled = program.pipe(
* Effect.catchReason("AiError", "RateLimitError", (reason) =>
* Effect.succeed(`Retry after ${reason.retryAfter}s`)
* )
* )
* ```
*
* @see {@link catchReasons} for handling several nested reason tags
*
* @category error handling
* @since 4.0.0
*/
export const catchReason = internal.catchReason;
/**
* Catches multiple reasons within a tagged error using an object of handlers.
*
* **Example** (Handling multiple error reasons)
*
* ```ts
* import { Data, Effect } from "effect"
*
* class RateLimitError extends Data.TaggedError("RateLimitError")<{
* retryAfter: number
* }> {}
*
* class QuotaExceededError extends Data.TaggedError("QuotaExceededError")<{
* limit: number
* }> {}
*
* class AiError extends Data.TaggedError("AiError")<{
* reason: RateLimitError | QuotaExceededError
* }> {}
*
* declare const program: Effect.Effect<string, AiError>
*
* const handled = program.pipe(
* Effect.catchReasons("AiError", {
* RateLimitError: (reason) =>
* Effect.succeed(`Retry after ${reason.retryAfter}s`),
* QuotaExceededError: (reason) =>
* Effect.succeed(`Quota exceeded: ${reason.limit}`)
* })
* )
* ```
*
* @category error handling
* @since 4.0.0
*/
export const catchReasons = internal.catchReasons;
/**
* Promotes nested reason errors into the Effect error channel, replacing
* the parent error.
*
* **Example** (Extracting the reason from a tagged error)
*
* ```ts
* import { Data, Effect } from "effect"
*
* class RateLimitError extends Data.TaggedError("RateLimitError")<{
* retryAfter: number
* }> {}
*
* class QuotaExceededError extends Data.TaggedError("QuotaExceededError")<{
* limit: number
* }> {}
*
* class AiError extends Data.TaggedError("AiError")<{
* reason: RateLimitError | QuotaExceededError
* }> {}
*
* declare const program: Effect.Effect<string, AiError>
*
* // Before: Effect<string, AiError>
* // After: Effect<string, RateLimitError | QuotaExceededError>
* const unwrapped = program.pipe(Effect.unwrapReason("AiError"))
* ```
*
* @category error handling
* @since 4.0.0
*/
export const unwrapReason = internal.unwrapReason;
/**
* Handles both recoverable and unrecoverable errors by providing a recovery
* effect.
*
* **When to use**
*
* Use when you need to recover from an `Effect` by inspecting the full `Cause`,
* including recoverable failures, defects, and interruptions, instead of only
* the typed error value.
*
* **Details**
*
* When to Recover from Defects:
*
* Defects are unexpected errors that typically shouldn't be recovered from, as
* they often indicate serious issues. However, in some cases, such as
* dynamically loaded plugins, controlled recovery might be needed.
*
* **Example** (Recovering from full failure causes)
*
* ```ts
* import { Cause, Console, Effect } from "effect"
*
* // An effect that might fail in different ways
* const program = Effect.die("Something went wrong")
*
* // Recover from any cause (including defects)
* const recovered = Effect.catchCause(program, (cause) => {
* if (Cause.hasDies(cause)) {
* return Console.log("Caught defect").pipe(
* Effect.as("Recovered from defect")
* )
* }
* return Effect.succeed("Unknown error")
* })
* ```
*
* @category error handling
* @since 4.0.0
*/
export const catchCause = internal.catchCause;
/**
* Recovers from defects using a provided recovery function.
*
* **When to use**
*
* Use when you need to report or translate defects at integration boundaries.
*
* **Details**
*
* `catchDefect` handles unexpected defects, such as thrown exceptions or
* values passed to `die`, without catching typed failures or interruptions.
*
* When to Recover from Defects:
*
* Defects are unexpected errors that typically should not be recovered from, as
* they often indicate serious issues. In some cases, such as dynamically loaded
* plugins, controlled recovery may be needed.
*
* **Example** (Recovering from defects)
*
* ```ts
* import { Console, Effect } from "effect"
*
* // An effect that might throw an unexpected error (defect)
* const program = Effect.sync(() => {
* throw new Error("Unexpected error")
* })
*
* // Recover from defects only
* const recovered = Effect.catchDefect(program, (defect) => {
* return Console.log(`Caught defect: ${defect}`).pipe(
* Effect.as("Recovered from defect")
* )
* })
* ```
*
* @category error handling
* @since 4.0.0
*/
export const catchDefect = internal.catchDefect;
/**
* Recovers from specific errors using a `Predicate` or `Refinement`.
*
* **When to use**
*
* Use when you need to recover from errors that match a condition.
*
* **Details**
*
* Use a `Refinement` for type narrowing or a `Predicate` for simple boolean
* matching. Non-matching errors re-fail with the original cause. Defects and
* interrupts are not caught.
*
* **Example** (Recovering when a predicate matches)
*
* ```ts
* import { Data, Effect, Filter } from "effect"
*
* class NotFound extends Data.TaggedError("NotFound")<{ id: string }> {}
*
* const program = Effect.fail(new NotFound({ id: "user-1" }))
*
* // With a refinement
* const recovered = program.pipe(
* Effect.catchIf(
* (error): error is NotFound => error._tag === "NotFound",
* (error) => Effect.succeed(`missing:${error.id}`)
* )
* )
*
* // With a Filter
* const recovered2 = program.pipe(
* Effect.catchFilter(
* Filter.tagged("NotFound"),
* (error) => Effect.succeed(`missing:${error.id}`)
* )
* )
* ```
*
* @category error handling
* @since 2.0.0
*/
export const catchIf = internal.catchIf;
/**
* Recovers from specific errors using a `Filter`.
*
* **When to use**
*
* Use to recover from typed `Effect` errors with a reusable `Filter` when
* matching can also narrow or transform the error before choosing the recovery
* effect.
*
* **Details**
*
* The filter runs on typed failures extracted from the `Cause`. Successful
* filter results are passed to `f`; failed filter results are passed to
* `orElse` when provided. Without `orElse`, the original failure cause is
* preserved.
*
* @see {@link catchIf} for predicate-based recovery from typed errors
* @see {@link catchTag} for recovering from a single tagged error
* @see {@link catchTags} for recovering from several tagged errors
* @see {@link catchCauseFilter} for filtering full causes instead of typed errors
*
* @category error handling
* @since 4.0.0
*/
export const catchFilter = internal.catchFilter;
/**
* Catches `NoSuchElementError` failures and converts them to `Option.none`.
*
* **When to use**
*
* Use when you expect missing-value failures and want them to become an
* optional success while all other failures keep failing.
*
* **Details**
*
* Success values become `Option.some`, `NoSuchElementError` becomes
* `Option.none`, and all other errors are preserved.
*
* **Example** (Recovering from missing Option values)
*
* ```ts
* import { Effect, Option } from "effect"
*
* const some = Effect.fromNullishOr(1).pipe(Effect.catchNoSuchElement)
* const none = Effect.fromNullishOr(null).pipe(Effect.catchNoSuchElement)
*
* Effect.runPromise(some).then(console.log) // { _id: 'Option', _tag: 'Some', value: 1 }
* Effect.runPromise(none).then(console.log) // { _id: 'Option', _tag: 'None' }
* ```
*
* @see {@link fromOption} for converting `Option.none` into `NoSuchElementError`
* @see {@link fromNullishOr} for converting nullish values into `NoSuchElementError`
* @see {@link option} for converting any failure into `Option.none`
*
* @category error handling
* @since 4.0.0
*/
export const catchNoSuchElement = internal.catchNoSuchElement;
/**
* Recovers from specific failures based on a predicate.
*
* **When to use**
*
* Use to recover an `Effect` from full causes selected by a predicate.
*
* **Details**
*
* This function allows you to conditionally catch and recover from failures
* that match a specific predicate. This is useful when you want to handle
* only certain types of errors while letting others propagate.
*
* **Example** (Recovering from selected causes)
*
* ```ts
* import { Cause, Console, Effect } from "effect"
*
* const httpRequest = Effect.fail("Network Error")
*
* // Only catch network-related failures
* const program = Effect.catchCauseIf(
* httpRequest,
* Cause.hasFails,
* (cause) =>
* Effect.gen(function*() {
* yield* Console.log(`Caught network error: ${Cause.squash(cause)}`)
* return "Fallback response"
* })
* )
*
* Effect.runPromise(program).then(console.log)
* // Output: "Caught network error: Network Error"
* // Then: "Fallback response"
* ```
*
* @see {@link catchCause} for recovering from every cause
* @see {@link catchCauseFilter} for selecting full causes with a `Filter`
* @see {@link catchIf} for predicate-based recovery from typed errors
*
* @category error handling
* @since 4.0.0
*/
export const catchCauseIf = internal.catchCauseIf;
/**
* Recovers from specific failures based on a `Filter`.
*
* **When to use**
*
* Use when you need to recover an `Effect` only from causes selected by a
* `Filter`, while giving the recovery both the selected value and the original
* `Cause`.
*
* **Details**
*
* The filter is applied to the full `Cause`. When it succeeds, the handler
* receives the selected value and the original cause. When it fails, the effect
* re-fails with the residual cause returned by the filter.
*
* @see {@link catchCauseIf} for predicate-based cause selection
* @see {@link catchFilter} for filtering typed error values instead of full causes
* @see {@link catchCause} for recovering from every cause without filtering
*
* @category error handling
* @since 4.0.0
*/
export const catchCauseFilter = internal.catchCauseFilter;
/**
* Transforms the failure value of an effect without changing its success value.
*
* **When to use**
*
* Use to translate an `Effect`'s typed failures while leaving successful values
* unchanged.
*
* **Details**
*
* Only the failure channel is transformed. The success channel and requirements
* are preserved.
*
* **Example** (Transforming the error channel)
*
* ```ts
* import { Data, Effect } from "effect"
*
* class TaskError extends Data.TaggedError("TaskError")<{ readonly message: string }> {}
*
* // ┌─── Effect<number, string, never>
* // ▼
* const simulatedTask = Effect.fail("Oh no!").pipe(Effect.as(1))
*
* // ┌─── Effect<number, TaskError, never>
* // ▼
* const mapped = Effect.mapError(
* simulatedTask,
* (message) => new TaskError({ message })
* )
* ```
*
* @see {@link map} for a version that operates on the success channel.
* @see {@link mapBoth} for a version that operates on both channels.
*
* @category error handling
* @since 2.0.0
*/
export const mapError = internal.mapError;
/**
* Applies transformations to both the success and error channels of an effect.
*
* **When to use**
*
* Use to transform both success and failure channels of an `Effect` without
* changing whether it succeeds or fails.
*
* **Details**
*
* This function takes two map functions as arguments: one for the error channel
* and one for the success channel. You can use it when you want to modify both
* the error and the success values without altering the overall success or
* failure status of the effect.
*
* **Example** (Transforming success and failure channels)
*
* ```ts
* import { Data, Effect } from "effect"
*
* class TaskError extends Data.TaggedError("TaskError")<{ readonly message: string }> {}
*
* // ┌─── Effect<number, string, never>
* // ▼
* const simulatedTask = Effect.fail("Oh no!").pipe(Effect.as(1))
*
* // ┌─── Effect<boolean, TaskError, never>
* // ▼
* const modified = Effect.mapBoth(simulatedTask, {
* onFailure: (message) => new TaskError({ message }),
* onSuccess: (n) => n > 0
* })
* ```
*
* @see {@link map} for a version that operates on the success channel.
* @see {@link mapError} for a version that operates on the error channel.
*
* @category mapping
* @since 2.0.0
*/
export const mapBoth = internal.mapBoth;
/**
* Converts typed failures from the error channel into defects, removing the
* error type from the returned effect.
*
* **When to use**
*
* Use when you need to turn an `Effect` typed failure that represents an
* unrecoverable bug or invalid state into a defect.
*
* **Example** (Converting typed failures into defects)
*
* ```ts
* import { Data, Effect } from "effect"
*
* class DivideByZeroError extends Data.TaggedError("DivideByZeroError")<{}> {}
*
* const divide = (a: number, b: number) =>
* b === 0
* ? Effect.fail(new DivideByZeroError())
* : Effect.succeed(a / b)
*
* // ┌─── Effect<number, never, never>
* // ▼
* const program = Effect.orDie(divide(1, 0))
*
* Effect.runPromise(program).catch(console.error)
* // Output:
* // (FiberFailure) DivideByZeroError
* // ...stack trace...
* ```
*
* @category converting failures to defects
* @since 2.0.0
*/
export const orDie = internal.orDie;
/**
* Runs an effectful operation when the source effect fails, while preserving
* the original failure when the operation succeeds.
*
* **Details**
*
* Use this for logging, metrics, or other failure-side observations. If the
* operation passed to `tapError` fails, that error is also represented in the
* returned effect's error channel.
*
* **Example** (Running effects on failure)
*
* ```ts
* import { Console, Effect } from "effect"
*
* // Simulate a task that fails with an error
* const task: Effect.Effect<number, string> = Effect.fail("NetworkError")
*
* // Use tapError to log the error message when the task fails
* const tapping = Effect.tapError(
* task,
* (error) => Console.log(`expected error: ${error}`)
* )
*
* Effect.runFork(tapping)
* // Output:
* // expected error: NetworkError
* ```
*
* @category sequencing
* @since 2.0.0
*/
export const tapError = internal.tapError;
/**
* Runs an effectful handler when a failure's `_tag` matches.
*
* **Details**
*
* Use this with tagged-union errors to perform side effects for one tag or a
* list of tags. When the handler succeeds, the original failure is preserved;
* if the handler fails, its error is also included in the returned effect.
*
* **Example** (Running effects for tagged failures)
*
* ```ts
* import { Console, Data, Effect } from "effect"
*
* class NetworkError extends Data.TaggedError("NetworkError")<{
* statusCode: number
* }> {}
*
* class ValidationError extends Data.TaggedError("ValidationError")<{
* field: string
* }> {}
*
* const task: Effect.Effect<number, NetworkError | ValidationError> =
* Effect.fail(new NetworkError({ statusCode: 504 }))
*
* const program = Effect.tapErrorTag(task, "NetworkError", (error) =>
* Console.log(`expected error: ${error.statusCode}`)
* )
*
* Effect.runPromiseExit(program)
* // Output:
* // expected error: 504
* ```
*
* @category sequencing
* @since 2.0.0
*/
export const tapErrorTag = internal.tapErrorTag;
/**
* Runs an effectful operation with the full `Cause` when the source effect
* fails.
*
* **When to use**
*
* Use when failure observation needs typed failures, defects, and interruptions
* rather than only the typed error value.
*
* **Details**
*
* Use this to log or inspect typed failures, defects, and interruptions. When
* the operation succeeds, the original cause is preserved. If the operation
* fails, its error is also represented in the returned effect.
*
* **Example** (Observing full failure causes)
*
* ```ts
* import { Cause, Console, Effect } from "effect"
*
* const task = Effect.fail("Something went wrong")
*
* const program = Effect.tapCause(
* task,
* (cause) => Console.log(`Logging cause: ${Cause.squash(cause)}`)
* )
*
* Effect.runPromiseExit(program).then(console.log)
* // Output: "Logging cause: Error: Something went wrong"
* // Then: { _id: 'Exit', _tag: 'Failure', cause: ... }
* ```
*
* @category sequencing
* @since 4.0.0
*/
export const tapCause = internal.tapCause;
/**
* Executes a side effect conditionally when a failed effect's cause matches a predicate.
*
* **Details**
*
* This function allows you to tap into the cause of an effect's failure only when
* the cause matches a specific predicate. This is useful for conditional logging,
* monitoring, or other side effects based on the type of failure.
*
* **Example** (Observing selected failure causes)
*
* ```ts
* import { Cause, Console, Effect } from "effect"
*
* const task = Effect.fail("Network timeout")
*
* // Only log causes that contain failures (not interrupts or defects)
* const program = Effect.tapCauseIf(
* task,
* Cause.hasFails,
* (cause) => Console.log(`Logging failure cause: ${Cause.squash(cause)}`)
* )
*
* Effect.runPromiseExit(program).then(console.log)
* // Output: "Logging failure cause: Network timeout"
* // Then: { _id: 'Exit', _tag: 'Failure', cause: ... }
* ```
*
* @category sequencing
* @since 4.0.0
*/
export const tapCauseIf = internal.tapCauseIf;
/**
* Executes a side effect conditionally when a failed effect's cause passes a filter.
*
* **When to use**
*
* Use when you need to observe only failure causes selected by a `Filter`,
* while giving the side effect both the selected value and the original
* `Cause`.
*
* **Details**
*
* A successful filter result runs the side effect with the selected value and
* original cause. A failed filter result skips the side effect and preserves the
* original cause.
*
* @see {@link tapCauseIf} for selecting causes with a boolean predicate
* @see {@link tapCause} for observing every failure cause
* @see {@link catchCauseFilter} for recovering from selected causes instead of only observing them
*
* @category sequencing
* @since 4.0.0
*/
export const tapCauseFilter = internal.tapCauseFilter;
/**
* Runs an effectful operation when the source effect dies with a defect.
*
* **Details**
*
* Use this for diagnostics such as logging unexpected thrown exceptions or
* values passed to `die`. Recoverable failures are not handled. When the
* operation succeeds, the original defect is preserved; if the operation fails,
* its error is also represented in the returned effect.
*
* **Example** (Observing defects)
*
* ```ts
* import { Console, Effect } from "effect"
*
* // Simulate a task that fails with a recoverable error
* const task1: Effect.Effect<number, string> = Effect.fail("NetworkError")
*
* // tapDefect won't log anything because NetworkError is not a defect
* const tapping1 = Effect.tapDefect(
* task1,
* (cause) => Console.log(`defect: ${cause}`)
* )
*
* Effect.runFork(tapping1)
* // No Output
*
* // Simulate a severe failure in the system
* const task2: Effect.Effect<number> = Effect.die(
* "Something went wrong"
* )
*
* // Log the defect using tapDefect
* const tapping2 = Effect.tapDefect(
* task2,
* (cause) => Console.log(`defect: ${cause}`)
* )
*
* Effect.runFork(tapping2)
* // Output:
* // defect: RuntimeException: Something went wrong
* // ... stack trace ...
* ```
*
* @category sequencing
* @since 2.0.0
*/
export const tapDefect = internal.tapDefect;
/**
* Retries an effect until it succeeds, discarding failures.
*
* **Details**
*
* Yields between attempts so other fibers can run.
*
* **Example** (Retrying until success)
*
* ```ts
* import { Console, Effect } from "effect"
*
* let attempts = 0
*
* const flaky = Effect.gen(function*() {
* attempts++
* yield* Console.log(`Attempt ${attempts}`)
* if (attempts < 3) {
* return yield* Effect.fail("Not ready")
* }
* return "Ready"
* })
*
* const program = Effect.eventually(flaky)
*
* Effect.runPromise(program).then(console.log)
* // Output:
* // Attempt 1
* // Attempt 2
* // Attempt 3
* // Ready
* ```
*
* @category repetition
* @since 2.0.0
*/
export const eventually = internal.eventually;
/**
* Retries typed failures from an effect according to a retry policy.
*
* **When to use**
*
* Use when you need to rerun an effect after transient typed failures, such as
* network issues or temporary resource unavailability.
*
* **Details**
*
* The policy can be a `Schedule`, a schedule builder, or a `Retry.Options`
* object using `schedule`, `times`, `while`, or `until`. If a retry eventually
* succeeds, the returned effect succeeds with that value. If the policy stops
* while the effect is still failing, the last failure is propagated.
*
* **Gotchas**
*
* The source effect is always evaluated once before any retry policy is
* applied. For example, `Schedule.recurs(3)` allows up to three retries after
* the initial attempt.
*
* Defects and interruptions are not retried.
*
* **Example** (Retrying with a schedule)
*
* ```ts
* import { Data, Effect, Schedule } from "effect"
*
* class AttemptError extends Data.TaggedError("AttemptError")<{ readonly attempt: number }> {}
*
* let attempt = 0
* const task = Effect.callback<string, AttemptError>((resume) => {
* attempt++
* if (attempt <= 2) {
* resume(Effect.fail(new AttemptError({ attempt })))
* } else {
* resume(Effect.succeed("Success!"))
* }
* })
*
* const policy = Schedule.addDelay(Schedule.recurs(5), () => Effect.succeed("100 millis"))
* const program = Effect.retry(task, policy)
*
* Effect.runPromise(program).then(console.log)
* // Output: "Success!" (after 2 retries)
* ```
*
* @see {@link retryOrElse} for a version that allows you to run a fallback.
* @see {@link repeat} if your retry condition is based on successful outcomes rather than errors.
* @category error handling
* @since 2.0.0
*/
export const retry = internalSchedule.retry;
/**
* Retries a failing effect and runs a fallback effect if retries are exhausted.
*
* **When to use**
*
* Use when you want to handle failures gracefully by specifying an alternative
* action after repeated failures.
*
* **Details**
*
* The `Effect.retryOrElse` function attempts to retry a failing effect multiple
* times according to a defined {@link Schedule} policy.
*
* If the retries are exhausted and the effect still fails, it runs a fallback
* effect instead.
*
* **Example** (Falling back after retries are exhausted)
*
* ```ts
* import { Console, Data, Effect, Schedule } from "effect"
*
* class NetworkTimeoutError extends Data.TaggedError("NetworkTimeoutError")<{}> {}
*
* let attempt = 0
* const networkRequest = Effect.gen(function*() {
* attempt++
* yield* Console.log(`Network attempt ${attempt}`)
* if (attempt < 3) {
* return yield* Effect.fail(new NetworkTimeoutError())
* }
* return "Network data"
* })
*
* // Retry up to 2 times, then fall back to cached data
* const program = Effect.retryOrElse(
* networkRequest,
* Schedule.recurs(2),
* (error, retryCount) =>
* Effect.gen(function*() {
* yield* Console.log(`All ${retryCount} retries failed, using cache`)
* return "Cached data"
* })
* )
*
* Effect.runPromise(program).then(console.log)
* // Output:
* // Network attempt 1
* // Network attempt 2
* // Network attempt 3
* // Network data
* ```
*
* @see {@link retry} for a version that does not run a fallback effect.
* @category error handling
* @since 2.0.0
*/
export const retryOrElse = internalSchedule.retryOrElse;
/**
* Exposes an effect's full failure cause in the error channel as `Cause<E>`.
*
* **Details**
*
* Use `sandbox` when downstream error handling needs to distinguish typed
* failures, defects, and interruptions. Use `unsandbox` to restore the original
* typed error channel after cause-level handling.
*
* **Example** (Exposing failures as causes)
*
* ```ts
* import { Cause, Effect } from "effect"
*
* const task = Effect.fail("Something went wrong")
*
* // Sandbox exposes the full cause as the error type
* const program = Effect.gen(function*() {
* const result = yield* Effect.flip(Effect.sandbox(task))
* return `Caught cause: ${Cause.squash(result)}`
* })
*
* Effect.runPromise(program).then(console.log)
* // Output: "Caught cause: Something went wrong"
* ```
*
* @category error handling
* @since 2.0.0
*/
export const sandbox = internal.sandbox;
/**
* Discards both the success and failure values of an effect.
*
* **When to use**
*
* Use when an effect should run for its side effects while both success and
* failure values are discarded.
*
* **Details**
*
* Use the `log` option to emit the full {@link Cause} when the effect fails,
* and `message` to prepend a custom log message.
*
* **Example** (Discarding success and failure values)
*
* ```ts
* import { Effect } from "effect"
*
* // ┌─── Effect<number, string, never>
* // ▼
* const task = Effect.fail("Uh oh!").pipe(Effect.as(5))
*
* // ┌─── Effect<void, never, never>
* // ▼
* const program = task.pipe(Effect.ignore)
* ```
*
* **Example** (Logging failures while ignoring results)
*
* ```ts
* import { Effect } from "effect"
*
* const task = Effect.fail("Uh oh!")
*
* const program = task.pipe(Effect.ignore({ log: true }))
* const programWarn = task.pipe(Effect.ignore({ log: "Warn", message: "Ignoring task failure" }))
* ```
*
* @category error handling
* @since 2.0.0
*/
export const ignore = internal.ignore;
/**
* Ignores the effect's failure cause, including defects and interruptions.
*
* **When to use**
*
* Use when a best-effort effect should never fail, even from defects or
* interruption, and optional cause logging is enough.
*
* **Details**
*
* Use the `log` option to emit the full {@link Cause} when the effect fails,
* and `message` to prepend a custom log message.
*
* **Example** (Ignoring failures and logging causes)
*
* ```ts
* import { Effect } from "effect"
*
* const task = Effect.fail("boom")
*
* const program = task.pipe(Effect.ignoreCause)
* const programLog = task.pipe(Effect.ignoreCause({ log: true, message: "Ignoring failure cause" }))
* ```
*
* @category error handling
* @since 4.0.0
*/
export const ignoreCause = internal.ignoreCause;
/**
* Applies an `ExecutionPlan` to an effect, retrying with step-provided resources
* until it succeeds or the plan is exhausted.
*
* **Details**
*
* Each attempt updates `ExecutionPlan.CurrentMetadata` (attempt and step index),
* and retry timing is derived per step (the first attempt uses the remaining
* attempts schedule; later retries apply the step schedule at least once).
*
* **Example** (Retrying with an execution plan)
*
* ```ts
* import { Context, Effect, ExecutionPlan, Layer } from "effect"
*
* const Endpoint = Context.Service<{ url: string }>("Endpoint")
*
* const fetchUrl = Effect.gen(function*() {
* const endpoint = yield* Effect.service(Endpoint)
* if (endpoint.url === "bad") {
* return yield* Effect.fail("Unavailable")
* }
* return endpoint.url
* })
*
* const plan = ExecutionPlan.make(
* { provide: Layer.succeed(Endpoint, { url: "bad" }), attempts: 2 },
* { provide: Layer.succeed(Endpoint, { url: "good" }) }
* )
*
* const program = Effect.withExecutionPlan(fetchUrl, plan)
* ```
*
* @category fallback
* @since 3.16.0
*/
export const withExecutionPlan = internalExecutionPlan.withExecutionPlan;
/**
* Runs an effect and reports any errors to the configured `ErrorReporter`s.
*
* **Details**
*
* If the `defectsOnly` option is set to `true`, only defects (unrecoverable
* errors) will be reported, while regular failures will be ignored.
*
* @category error handling
* @since 4.0.0
*/
export const withErrorReporting = internal.withErrorReporting;
// -----------------------------------------------------------------------------
// Fallback
// -----------------------------------------------------------------------------
/**
* Recovers from a typed failure by producing a fallback success value.
*
* **Details**
*
* If the source effect succeeds, its value is preserved. If it fails in the
* error channel, `orElseSucceed` evaluates the fallback and succeeds with that
* value, removing the typed error from the returned effect.
*
* Defects and interruptions are not recovered by this operator.
*
* **Example** (Replacing failures with a value)
*
* ```ts
* import { Effect } from "effect"
*
* const validate = (age: number): Effect.Effect<number, string> => {
* if (age < 0) {
* return Effect.fail("NegativeAgeError")
* } else if (age < 18) {
* return Effect.fail("IllegalAgeError")
* } else {
* return Effect.succeed(age)
* }
* }
*
* const program = Effect.orElseSucceed(validate(-1), () => 18)
*
* console.log(Effect.runSyncExit(program))
* // Output:
* // { _id: 'Exit', _tag: 'Success', value: 18 }
* ```
*
* @category fallback
* @since 2.0.0
*/
export const orElseSucceed = internal.orElseSucceed;
/**
* Runs a sequence of effects and returns the result of the first successful
* one.
*
* **When to use**
*
* Use when you have prioritized fallback `Effect`s, such as attempting
* multiple APIs, reading configuration from several sources, or trying
* alternative resource locations in order.
*
* **Details**
*
* This function executes the provided effects in sequence, stopping at the
* first success. If an effect succeeds, its result is returned immediately and
* no further effects in the sequence are executed.
*
* If all effects fail, the returned effect fails with the error from the last
* effect. If the collection is empty, the returned effect defects with an
* `Error` whose message is `"Received an empty collection of effects"`.
*
* **Example** (Trying alternatives until one succeeds)
*
* ```ts
* import { Effect } from "effect"
*
* const primary = Effect.fail("primary unavailable")
* const secondary = Effect.succeed("secondary result")
* const tertiary = Effect.sync(() => {
* throw new Error("not evaluated")
* })
*
* const program = Effect.firstSuccessOf([
* primary,
* secondary,
* tertiary
* ])
*
* console.log(Effect.runSync(program))
* // Output: "secondary result"
* ```
*
* @category fallback
* @since 2.0.0
*/
export const firstSuccessOf = internal.firstSuccessOf;
// -----------------------------------------------------------------------------
// Delays & timeouts
// -----------------------------------------------------------------------------
/**
* Adds a time limit to an effect, triggering a timeout if the effect exceeds
* the duration.
*
* **When to use**
*
* Use when you need a timeout of an `Effect` to be represented as a typed
* failure.
*
* **Details**
*
* The `timeout` function allows you to specify a time limit for an
* effect's execution. If the effect does not complete within the given time, a
* `TimeoutException` is raised. This can be useful for controlling how long
* your program waits for a task to finish, ensuring that it doesn't hang
* indefinitely if the task takes too long.
*
* **Gotchas**
*
* If the timeout wins, the source effect is interrupted.
*
* **Example** (Failing when work takes too long)
*
* ```ts
* import { Effect } from "effect"
*
* const task = Effect.gen(function*() {
* console.log("Start processing...")
* yield* Effect.sleep("2 seconds") // Simulates a delay in processing
* console.log("Processing complete.")
* return "Result"
* })
*
* // Output will show a TimeoutException as the task takes longer
* // than the specified timeout duration
* const timedEffect = task.pipe(Effect.timeout("1 second"))
*
* Effect.runPromiseExit(timedEffect).then(console.log)
* // Output:
* // Start processing...
* // {
* // _id: 'Exit',
* // _tag: 'Failure',
* // cause: {
* // _id: 'Cause',
* // _tag: 'Fail',
* // failure: { _tag: 'TimeoutException' }
* // }
* // }
* ```
*
* @see {@link timeoutOption} for returning `Option.none` on timeout.
* @see {@link timeoutOrElse} for a version that allows specifying both success and timeout handlers.
*
* @category delays & timeouts
* @since 2.0.0
*/
export const timeout = internal.timeout;
/**
* Runs an effect with a time limit and represents only the timeout case as
* `Option.none`.
*
* **When to use**
*
* Use when a timeout of an `Effect` should be handled as `Option.none`.
*
* **Details**
*
* If the source effect succeeds before the timeout, the returned effect
* succeeds with `Option.some(value)`. If the timeout wins, the source effect is
* interrupted and the returned effect succeeds with `Option.none`. If the
* source effect fails before the timeout, that failure is preserved.
*
* **Example** (Returning None on timeout)
*
* ```ts
* import { Effect } from "effect"
*
* const task = Effect.gen(function*() {
* console.log("Start processing...")
* yield* Effect.sleep("2 seconds") // Simulates a delay in processing
* console.log("Processing complete.")
* return "Result"
* })
*
* const timedOutEffect = Effect.all([
* task.pipe(Effect.timeoutOption("3 seconds")),
* task.pipe(Effect.timeoutOption("1 second"))
* ])
*
* Effect.runPromise(timedOutEffect).then(console.log)
* // Output:
* // Start processing...
* // Processing complete.
* // Start processing...
* // [
* // { _id: 'Option', _tag: 'Some', value: 'Result' },
* // { _id: 'Option', _tag: 'None' }
* // ]
* ```
*
* @see {@link timeout} for a version that raises a `TimeoutException`.
* @see {@link timeoutOrElse} for a version that allows specifying both success and timeout handlers.
*
* @category delays & timeouts
* @since 3.1.0
*/
export const timeoutOption = internal.timeoutOption;
/**
* Applies a timeout to an effect, with a fallback effect executed if the timeout is reached.
*
* **When to use**
*
* Use when a timeout of an `Effect` should switch to a fallback effect.
*
* **Details**
*
* The fallback effect is created lazily by `orElse` and may introduce its own
* success, failure, and requirement types.
*
* **Gotchas**
*
* If the timeout wins, the source effect is interrupted before the fallback is
* run.
*
* **Example** (Falling back on timeout)
*
* ```ts
* import { Console, Effect } from "effect"
*
* const slowQuery = Effect.gen(function*() {
* yield* Console.log("Starting database query...")
* yield* Effect.sleep("5 seconds")
* return "Database result"
* })
*
* // Use cached data as fallback when timeout is reached
* const program = Effect.timeoutOrElse(slowQuery, {
* duration: "2 seconds",
* orElse: () =>
* Effect.gen(function*() {
* yield* Console.log("Query timed out, using cached data")
* return "Cached result"
* })
* })
*
* Effect.runPromise(program).then(console.log)
* // Output:
* // Starting database query...
* // Query timed out, using cached data
* // Cached result
* ```
*
* @see {@link timeout} for failing with a `TimeoutException`.
* @see {@link timeoutOption} for returning `Option.none` on timeout.
*
* @category delays & timeouts
* @since 4.0.0
*/
export const timeoutOrElse = internal.timeoutOrElse;
/**
* Returns an effect that is delayed from this effect by the specified
* `Duration`.
*
* **Example** (Delaying an effect)
*
* ```ts
* import { Console, Effect } from "effect"
*
* const program = Effect.delay(
* Console.log("Delayed message"),
* "1 second"
* )
*
* Effect.runFork(program)
* // Waits 1 second, then prints: "Delayed message"
* ```
*
* @category delays & timeouts
* @since 2.0.0
*/
export const delay = internal.delay;
/**
* Returns an effect that suspends the current fiber for the specified duration
* without blocking a JavaScript thread.
*
* **Example** (Pausing without blocking)
*
* ```ts
* import { Console, Effect } from "effect"
*
* const program = Effect.gen(function*() {
* yield* Console.log("Start")
* yield* Effect.sleep("2 seconds")
* yield* Console.log("End")
* })
*
* Effect.runFork(program)
* // Output: "Start" (immediately)
* // Output: "End" (after 2 seconds)
* ```
*
* @category delays & timeouts
* @since 2.0.0
*/
export const sleep = internal.sleep;
/**
* Returns the runtime duration of an effect together with its result.
*
* **Details**
*
* The original success, failure, or interruption is preserved; only the success
* value is paired with the duration.
*
* **Example** (Measuring execution time)
*
* ```ts
* import { Console, Duration, Effect } from "effect"
*
* const program = Effect.gen(function*() {
* const [duration, value] = yield* Effect.timed(Effect.succeed("ok"))
* yield* Console.log(`took ${Duration.toMillis(duration)}ms: ${value}`)
* })
* ```
*
* @category delays & timeouts
* @since 2.0.0
*/
export const timed = internal.timed;
// -----------------------------------------------------------------------------
// Racing
// -----------------------------------------------------------------------------
/**
* Runs multiple effects concurrently and returns the first successful result.
*
* **When to use**
*
* Use when early failures should be ignored until a success occurs
* or all effects fail.
*
* **Details**
*
* Early failures do not finish the race; `raceAll` keeps waiting until one
* effect succeeds or every effect has failed. When one effect succeeds, the
* remaining effects are interrupted. If every effect fails, the returned effect
* fails with a cause containing the collected failure reasons.
*
* **Example** (Racing many effects)
*
* ```ts
* import { Duration, Effect } from "effect"
*
* // Multiple effects with different delays
* const effect1 = Effect.delay(Effect.succeed("Fast"), Duration.millis(100))
* const effect2 = Effect.delay(Effect.succeed("Slow"), Duration.millis(500))
* const effect3 = Effect.delay(Effect.succeed("Very Slow"), Duration.millis(1000))
*
* // Race all effects - the first to succeed wins
* const raced = Effect.raceAll([effect1, effect2, effect3])
*
* // Result: "Fast" (after ~100ms)
* ```
*
* @see {@link race} for a version that handles only two effects.
* @category racing
* @since 2.0.0
*/
export const raceAll = internal.raceAll;
/**
* Runs multiple effects concurrently and completes with the first effect to
* finish, whether it succeeds or fails.
*
* **Details**
*
* After the first effect completes, all remaining effects are interrupted. Use
* `raceAll` when early failures should be ignored until a success occurs or
* all effects fail.
*
* **Example** (Taking the first settled result)
*
* ```ts
* import { Duration, Effect } from "effect"
*
* // Multiple effects with different delays and potential failures
* const effect1 = Effect.delay(Effect.succeed("First"), Duration.millis(200))
* const effect2 = Effect.delay(Effect.fail("Second failed"), Duration.millis(100))
* const effect3 = Effect.delay(Effect.succeed("Third"), Duration.millis(300))
*
* // Race all effects - the first to succeed wins
* const raced = Effect.raceAllFirst([effect1, effect2, effect3])
*
* // Result: "First" (after ~200ms, even though effect2 completes first but fails)
* ```
*
* @category racing
* @since 4.0.0
*/
export const raceAllFirst = internal.raceAllFirst;
/**
* Races two effects and returns the first successful result.
*
* **Details**
*
* If one effect succeeds, the other is interrupted and `onWinner` can observe the
* winning fiber. If both fail, the race fails.
*
* **Example** (Racing two effects)
*
* ```ts
* import { Console, Duration, Effect } from "effect"
*
* const fastFail = Effect.delay(Effect.fail("fast-fail"), Duration.millis(10))
* const slowSuccess = Effect.delay(Effect.succeed("slow-success"), Duration.millis(50))
*
* const program = Effect.gen(function*() {
* const result = yield* Effect.race(fastFail, slowSuccess)
* yield* Console.log(`winner: ${result}`)
* })
*
* Effect.runPromise(program)
* // Output: winner: slow-success
* ```
*
* @category racing
* @since 2.0.0
*/
export const race = internal.race;
/**
* Races two effects and returns the result of the first one to complete, whether
* it succeeds or fails.
*
* **When to use**
*
* Use when any completion, including failure, should decide the race and
* interrupt the losing effect.
*
* **Details**
*
* The losing effect is interrupted, and `onWinner` can observe the winning fiber.
*
* **Example** (Observing the winning fiber)
*
* ```ts
* import { Console, Duration, Effect } from "effect"
*
* const fastFail = Effect.delay(Effect.fail("fast-fail"), Duration.millis(10))
* const slowSuccess = Effect.delay(Effect.succeed("slow-success"), Duration.millis(50))
*
* const program = Effect.gen(function*() {
* const message = yield* Effect.match(Effect.raceFirst(fastFail, slowSuccess), {
* onFailure: (error) => `failed: ${error}`,
* onSuccess: (value) => `succeeded: ${value}`
* })
* yield* Console.log(message)
* })
*
* Effect.runPromise(program)
* // Output: failed: fast-fail
* ```
*
* @category racing
* @since 2.0.0
*/
export const raceFirst = internal.raceFirst;
// -----------------------------------------------------------------------------
// Filtering
// -----------------------------------------------------------------------------
/**
* Filters elements of an iterable using a predicate, refinement, or effectful
* predicate.
*
* **Example** (Filtering success values)
*
* ```ts
* import { Effect } from "effect"
*
* // Sync predicate
* const evens = Effect.filter([1, 2, 3, 4], (n) => n % 2 === 0)
*
* // Effectful predicate
* const checked = Effect.filter([1, 2, 3], (n) => Effect.succeed(n > 1))
*
* // Use Effect.filterMapEffect for effectful Filter.Filter callbacks
* ```
*
* @category filtering
* @since 2.0.0
*/
export const filter = internal.filter;
/**
* Filters and maps elements of an iterable with a `Filter`.
*
* **When to use**
*
* Use when you need to filter an iterable with a `Filter` inside an `Effect`,
* collecting each filter success value.
*
* **Details**
*
* `Result.succeed` values are collected in the returned array, and
* `Result.fail` values are skipped.
*
* @see {@link filter} for keeping original elements with a boolean predicate, refinement, or effectful predicate
* @see {@link filterMapEffect} for using an effectful `Filter`
*
* @category filtering
* @since 2.0.0
*/
export const filterMap = internal.filterMap;
/**
* Filters and maps elements of an iterable effectfully with a `FilterEffect`.
*
* **When to use**
*
* Use when you need to filter each iterable element effectfully and transform
* accepted elements into successful output values.
*
* **Details**
*
* `Result.succeed` values are collected in the returned array, and
* `Result.fail` values are skipped.
*
* **Gotchas**
*
* With concurrent execution, successful values are collected in completion
* order, not input order.
*
* @see {@link filterMap} for using a synchronous `Filter`
* @see {@link filter} for keeping original elements with a predicate
*
* @category filtering
* @since 4.0.0
*/
export const filterMapEffect = internal.filterMapEffect;
/**
* Filters an effect, providing an alternative effect if the predicate fails.
*
* **When to use**
*
* Use when a successful value that fails a predicate should continue with an
* effectful fallback instead of failing the effect.
*
* **Details**
*
* This function applies a predicate to the result of an effect. If the
* predicate evaluates to `false`, it executes the `orElse` effect instead. The
* `orElse` effect can produce an alternative value or perform additional
* computations.
*
* **Example** (Filtering with a fallback effect)
*
* ```ts
* import { Effect } from "effect"
*
* // An effect that produces a number
* const program = Effect.succeed(5)
*
* // Filter for even numbers, provide alternative for odd numbers
* const filtered = Effect.filterOrElse(
* program,
* (n) => n % 2 === 0,
* (n) => Effect.succeed(`Number ${n} is odd`)
* )
*
* // Result: "Number 5 is odd" (since 5 is not even)
* ```
*
* @category filtering
* @since 2.0.0
*/
export const filterOrElse = internal.filterOrElse;
/**
* Filters an effect with a `Filter`, providing an alternative effect on failure.
*
* **When to use**
*
* Use when a successful effect value should be accepted and transformed by a
* `Filter`, while rejected values should continue with an alternative effect
* built from the filter failure.
*
* **Details**
*
* `Result.succeed` becomes the returned success value, and `Result.fail` is
* passed to `orElse`.
*
* @see {@link filterOrElse} for using a predicate and fallback effect
* @see {@link filterMapOrFail} for failing the effect when the filter fails
*
* @category filtering
* @since 4.0.0
*/
export const filterMapOrElse = internal.filterMapOrElse;
/**
* Filters an effect, failing with a custom error if the predicate fails.
*
* **Details**
*
* This function applies a predicate to the result of an effect. If the
* predicate evaluates to `false`, the effect fails with either a custom
* error (if `orFailWith` is provided) or a `NoSuchElementError`.
*
* **Example** (Filtering with a custom failure)
*
* ```ts
* import { Effect } from "effect"
*
* // An effect that produces a number
* const program = Effect.succeed(5)
*
* // Filter for even numbers, fail for odd numbers
* const filtered = Effect.filterOrFail(
* program,
* (n) => n % 2 === 0,
* (n) => `Expected even number, got ${n}`
* )
*
* // Result: Effect.fail("Expected even number, got 5")
* ```
*
* @category filtering
* @since 2.0.0
*/
export const filterOrFail = internal.filterOrFail;
/**
* Filters and maps an effect with a `Filter`, failing when the filter fails.
*
* **When to use**
*
* Use when validating and transforming one effect success with a synchronous
* `Filter`, while rejected values should fail the effect.
*
* **Details**
*
* `Result.succeed` becomes the returned success value. `Result.fail` is mapped
* with `orFailWith` when provided, or fails with `NoSuchElementError`.
*
* @see {@link filterMapOrElse} for continuing with a fallback effect when the filter fails
* @see {@link filterOrFail} for validating with a predicate instead of a `Filter`
* @see {@link filterMap} for filtering and mapping iterable elements
*
* @category filtering
* @since 4.0.0
*/
export const filterMapOrFail = internal.filterMapOrFail;
// -----------------------------------------------------------------------------
// Conditional Operators
// -----------------------------------------------------------------------------
/**
* Runs an effect conditionally based on the result of an effectful boolean
* condition.
*
* **When to use**
*
* Use when you need an effectful check to decide whether another effect should
* run while representing the skipped case explicitly.
*
* **Details**
*
* The condition effect is evaluated first. If it succeeds with `true`, the
* source effect is run and its success value is wrapped in `Option.some`. If it
* succeeds with `false`, the source effect is skipped and the result is
* `Option.none`. If the condition effect fails, that failure is preserved.
*
* **Example** (Conditionally running an effect)
*
* ```ts
* import { Console, Effect } from "effect"
*
* const shouldLog = true
*
* const program = Effect.when(
* Console.log("Condition is true!"),
* Effect.succeed(shouldLog)
* )
*
* Effect.runPromise(program).then(console.log)
* // Output: "Condition is true!"
* // { _id: 'Option', _tag: 'Some', value: undefined }
* ```
*
* @category conditional operators
* @since 2.0.0
*/
export const when = internal.when;
// -----------------------------------------------------------------------------
// Pattern matching
// -----------------------------------------------------------------------------
/**
* Handles both success and failure cases of an effect without performing side
* effects.
*
* **When to use**
*
* Use when you need to fold an `Effect` into a value by handling success and
* failure differently without triggering side effects.
*
* **Details**
*
* `match` lets you define custom handlers for both success and failure
* scenarios. You provide separate functions to handle each case, allowing you
* to process the result if the effect succeeds, or handle the error if the
* effect fails.
*
* **Example** (Matching success and failure values)
*
* ```ts
* import { Data, Effect } from "effect"
*
* class ExampleError extends Data.TaggedError("ExampleError")<{ readonly message: string }> {}
*
* const success: Effect.Effect<number, ExampleError> = Effect.succeed(42)
*
* const program1 = Effect.match(success, {
* onFailure: (error) => `failure: ${error.message}`,
* onSuccess: (value) => `success: ${value}`
* })
*
* // Run and log the result of the successful effect
* Effect.runPromise(program1).then(console.log)
* // Output: "success: 42"
*
* const failure: Effect.Effect<number, ExampleError> = Effect.fail(
* new ExampleError({ message: "Uh oh!" })
* )
*
* const program2 = Effect.match(failure, {
* onFailure: (error) => `failure: ${error.message}`,
* onSuccess: (value) => `success: ${value}`
* })
*
* // Run and log the result of the failed effect
* Effect.runPromise(program2).then(console.log)
* // Output: "failure: Uh oh!"
* ```
*
* @see {@link matchEffect} if you need to perform side effects in the handlers.
* @category pattern matching
* @since 2.0.0
*/
export const match = internal.match;
/**
* Handles both success and failure cases of an effect without performing side
* effects, with eager evaluation for resolved effects.
*
* **When to use**
*
* Use when you need to handle both success and failure cases of an
* already-resolved `Effect` with optimized handling.
*
* **Details**
*
* `matchEager` works like `match` but provides better performance for resolved
* effects (Success or Failure). When the effect is already resolved, it applies
* the handlers immediately without fiber scheduling. For unresolved effects,
* it falls back to the regular `match` behavior.
*
* **Example** (Pattern matching eagerly when possible)
*
* ```ts
* import { Effect } from "effect"
*
* const program = Effect.gen(function*() {
* const result = yield* Effect.matchEager(Effect.succeed(42), {
* onFailure: (error) => `Failed: ${error}`,
* onSuccess: (value) => `Success: ${value}`
* })
* console.log(result) // "Success: 42"
* })
* ```
*
* @see {@link match} for the non-eager version.
* @see {@link matchEffect} if you need to perform side effects in the handlers.
* @category pattern matching
* @since 4.0.0
*/
export const matchEager = internal.matchEager;
/**
* Handles failures by matching the cause of failure.
*
* **When to use**
*
* Use when you need to fold an `Effect` while the failure handler inspects the
* full `Cause`.
*
* **Details**
*
* The `matchCause` function allows you to handle failures with access to the
* full cause of the failure within a fiber.
*
* **Example** (Matching on success or failure causes)
*
* ```ts
* import { Cause, Effect } from "effect"
*
* const task = Effect.fail("Something went wrong")
*
* const program = Effect.matchCause(task, {
* onFailure: (cause) => `Failed: ${Cause.squash(cause)}`,
* onSuccess: (value) => `Success: ${value}`
* })
*
* Effect.runPromise(program).then(console.log)
* // Output: "Failed: Error: Something went wrong"
* ```
*
* @see {@link matchCauseEffect} if you need to perform side effects in the
* handlers.
* @see {@link match} if you don't need to handle the cause of the failure.
* @category pattern matching
* @since 2.0.0
*/
export const matchCause = internal.matchCause;
/**
* Handles failures by matching the cause of failure with eager evaluation.
*
* **When to use**
*
* Use when you expect an `Effect` to already be resolved and want to match the
* `Cause` without regular effect pipeline overhead.
*
* **Details**
*
* `matchCauseEager` works like `matchCause` but provides better performance for resolved
* effects by immediately applying the matching function instead of deferring it
* through the effect pipeline.
*
* **Example** (Eagerly matching already completed effects)
*
* ```ts
* import { Effect } from "effect"
*
* const handleResult = Effect.matchCauseEager(Effect.succeed(42), {
* onSuccess: (value) => `Success: ${value}`,
* onFailure: (cause) => `Failed: ${cause}`
* })
* ```
*
* @category pattern matching
* @since 4.0.0
*/
export const matchCauseEager = internal.matchCauseEager;
/**
* Handles success or failure eagerly with effectful handlers when the effect is already resolved.
*
* **When to use**
*
* Use when you need effectful success and cause-aware failure handlers for
* `Effect` inputs that may already be resolved.
*
* **Details**
*
* If the effect is an `Exit`, the matching handler runs immediately; otherwise it behaves like
* {@link matchCauseEffect}.
*
* @see {@link matchCauseEffect} for the non-eager effectful variant
* @see {@link matchCauseEager} for eager cause matching with pure handlers
* @see {@link matchEffect} for effectful matching on typed failures instead of full causes
*
* @category pattern matching
* @since 4.0.0
*/
export const matchCauseEffectEager = internal.matchCauseEffectEager;
/**
* Handles failures with access to the cause and allows performing side effects.
*
* **When to use**
*
* Use when you need to fold an `Effect` with effectful success handlers and
* `Cause`-aware failure handlers.
*
* **Details**
*
* The `matchCauseEffect` function works similarly to {@link matchCause}, but it
* also allows you to perform additional side effects based on the failure
* cause. This function provides access to the complete cause of the failure,
* making it possible to differentiate between various failure types, and allows
* you to respond accordingly while performing side effects (like logging or
* other operations).
*
* **Example** (Effectfully matching on causes)
*
* ```ts
* import { Cause, Console, Data, Effect, Result } from "effect"
*
* class TaskError extends Data.TaggedError("TaskError")<{ readonly message: string }> {}
*
* const task = Effect.fail(new TaskError({ message: "Task failed" }))
*
* const program = Effect.matchCauseEffect(task, {
* onFailure: (cause) =>
* Effect.gen(function*() {
* if (Cause.hasFails(cause)) {
* const error = Cause.findError(cause)
* if (Result.isSuccess(error)) {
* yield* Console.log(`Handling error: ${error.success.message}`)
* }
* return "recovered from error"
* } else {
* yield* Console.log("Handling interruption or defect")
* return "recovered from interruption/defect"
* }
* }),
* onSuccess: (value) =>
* Effect.gen(function*() {
* yield* Console.log(`Success: ${value}`)
* return `processed ${value}`
* })
* })
*
* Effect.runPromise(program).then(console.log)
* // Output:
* // Handling error: Task failed
* // recovered from error
* ```
*
* @see {@link matchCause} if you don't need side effects and only want to handle the result or failure.
* @see {@link matchEffect} if you don't need to handle the cause of the failure.
*
* @category pattern matching
* @since 2.0.0
*/
export const matchCauseEffect = internal.matchCauseEffect;
/**
* Handles both success and failure by running effectful handlers.
*
* **When to use**
*
* Use when you need to handle an `Effect`'s failure or success with handlers
* that return effects.
*
* **Details**
*
* Use `matchEffect` when either branch needs to return an `Effect`, such as
* performing logging, recovery, notification, or other effectful work. The
* returned effect succeeds or fails according to the handler that is run.
*
* **Example** (Matching success and failure with effectful handlers)
*
* ```ts
* import { Data, Effect } from "effect"
*
* class ExampleError extends Data.TaggedError("ExampleError")<{ readonly message: string }> {}
*
* const success: Effect.Effect<number, ExampleError> = Effect.succeed(42)
* const failure: Effect.Effect<number, ExampleError> = Effect.fail(
* new ExampleError({ message: "Uh oh!" })
* )
*
* const program1 = Effect.matchEffect(success, {
* onFailure: (error) =>
* Effect.succeed(`failure: ${error.message}`).pipe(
* Effect.tap(Effect.log)
* ),
* onSuccess: (value) =>
* Effect.succeed(`success: ${value}`).pipe(Effect.tap(Effect.log))
* })
*
* console.log(Effect.runSync(program1))
* // Output:
* // timestamp=... level=INFO fiber=#0 message="success: 42"
* // success: 42
*
* const program2 = Effect.matchEffect(failure, {
* onFailure: (error) =>
* Effect.succeed(`failure: ${error.message}`).pipe(
* Effect.tap(Effect.log)
* ),
* onSuccess: (value) =>
* Effect.succeed(`success: ${value}`).pipe(Effect.tap(Effect.log))
* })
*
* console.log(Effect.runSync(program2))
* // Output:
* // timestamp=... level=INFO fiber=#1 message="failure: Uh oh!"
* // failure: Uh oh!
* ```
*
* @see {@link match} if you don't need side effects and only want to handle the
* result or failure.
* @category pattern matching
* @since 2.0.0
*/
export const matchEffect = internal.matchEffect;
// -----------------------------------------------------------------------------
// Condition checking
// -----------------------------------------------------------------------------
/**
* Determines whether an effect fails.
*
* **Details**
*
* Defects are not converted; if the effect dies, the resulting effect dies too.
*
* **Example** (Checking whether an effect fails)
*
* ```ts
* import { Console, Effect } from "effect"
*
* const program = Effect.gen(function*() {
* const failed = yield* Effect.isFailure(Effect.fail("Uh oh!"))
* yield* Console.log(failed)
* })
*
* Effect.runPromise(program)
* // Output: true
* ```
*
* @category condition checking
* @since 2.0.0
*/
export const isFailure = internal.isFailure;
/**
* Returns whether an effect completes successfully.
*
* **Details**
*
* Returns `false` for failures in the error channel, but defects still fail the
* effect.
*
* **Example** (Checking whether an effect succeeds)
*
* ```ts
* import { Console, Effect } from "effect"
*
* const program = Effect.gen(function*() {
* const ok = yield* Effect.isSuccess(Effect.succeed("done"))
* const failed = yield* Effect.isSuccess(Effect.fail("Uh oh"))
* yield* Console.log(`ok: ${ok}`)
* yield* Console.log(`failed: ${failed}`)
* })
*
* Effect.runPromise(program)
* // Output:
* // ok: true
* // failed: false
* ```
*
* @category condition checking
* @since 2.0.0
*/
export const isSuccess = internal.isSuccess;
// -----------------------------------------------------------------------------
// Environment
// -----------------------------------------------------------------------------
/**
* Returns the complete context.
*
* **When to use**
*
* Use to read the complete `Context` available to the current effect.
*
* **Details**
*
* This function allows you to access all services that are currently available
* in the effect's environment. This can be useful for debugging, introspection,
* or when you need to pass the entire context to another function.
*
* **Example** (Reading the full context)
*
* ```ts
* import { Console, Context, Effect, Option } from "effect"
*
* const Logger = Context.Service<{
* log: (msg: string) => void
* }>("Logger")
* const Database = Context.Service<{
* query: (sql: string) => string
* }>("Database")
*
* const program = Effect.gen(function*() {
* const allServices = yield* Effect.context()
*
* // Check if specific services are available
* const loggerOption = Context.getOption(allServices, Logger)
* const databaseOption = Context.getOption(allServices, Database)
*
* yield* Console.log(`Logger available: ${Option.isSome(loggerOption)}`)
* yield* Console.log(`Database available: ${Option.isSome(databaseOption)}`)
* })
*
* const context = Context.make(Logger, { log: console.log })
* .pipe(Context.add(Database, { query: () => "result" }))
*
* const provided = Effect.provideContext(program, context)
* ```
*
* @see {@link contextWith} for deriving an effect from the complete context
* @see {@link service} for reading one service from the context
*
* @category environment
* @since 2.0.0
*/
export const context = internal.context;
/**
* Transforms the current context using the provided function.
*
* **When to use**
*
* Use to derive an effect from the complete `Context`.
*
* **Details**
*
* This function allows you to access the complete context and perform
* computations based on all available services. This is useful when you need
* to conditionally execute logic based on what services are available.
*
* **Example** (Deriving values from the context)
*
* ```ts
* import { Console, Context, Effect, Option } from "effect"
*
* const Logger = Context.Service<{
* log: (msg: string) => void
* }>("Logger")
* const Cache = Context.Service<{
* get: (key: string) => string | null
* }>("Cache")
*
* const program = Effect.contextWith((services) => {
* const cacheOption = Context.getOption(services, Cache)
* const hasCache = Option.isSome(cacheOption)
*
* if (hasCache) {
* return Effect.gen(function*() {
* const cache = yield* Effect.service(Cache)
* yield* Console.log("Using cached data")
* return cache.get("user:123") || "default"
* })
* } else {
* return Effect.gen(function*() {
* yield* Console.log("No cache available, using fallback")
* return "fallback data"
* })
* }
* })
*
* const withCache = Effect.provideService(program, Cache, {
* get: () => "cached_value"
* })
* ```
*
* @see {@link context} for reading the complete context as a value
* @see {@link service} for reading one service from the context
*
* @category environment
* @since 2.0.0
*/
export const contextWith = internal.contextWith;
/**
* Provides dependencies to an effect using layers or a context. Use `options.local`
* to build the layer every time; by default, layers are shared between provide
* calls.
*
* **Example** (Providing dependencies with a layer)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* interface Database {
* readonly query: (sql: string) => Effect.Effect<string>
* }
*
* const Database = Context.Service<Database>("Database")
*
* const DatabaseLive = Layer.succeed(Database)({
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`Result for: ${sql}`))
* })
*
* const program = Effect.gen(function*() {
* const db = yield* Database
* return yield* db.query("SELECT * FROM users")
* })
*
* const provided = Effect.provide(program, DatabaseLive)
*
* Effect.runPromise(provided).then(console.log)
* // Output: "Result for: SELECT * FROM users"
* ```
*
* @category environment
* @since 2.0.0
*/
export const provide = internalLayer.provide;
/**
* Provides a context to an effect, fulfilling its service requirements.
*
* **Details**
*
* This function provides multiple services at once by supplying a context
* that contains all the required services. It removes the provided services
* from the effect's requirements, making them available to the effect.
*
* **Example** (Providing a complete context)
*
* ```ts
* import { Context, Effect } from "effect"
*
* // Define service keys
* const Logger = Context.Service<{
* log: (msg: string) => void
* }>("Logger")
* const Database = Context.Service<{
* query: (sql: string) => string
* }>("Database")
*
* // Create a context with multiple services
* const context = Context.make(Logger, { log: console.log })
* .pipe(Context.add(Database, { query: () => "result" }))
*
* // An effect that requires both services
* const program = Effect.gen(function*() {
* const logger = yield* Effect.service(Logger)
* const db = yield* Effect.service(Database)
* logger.log("Querying database")
* return db.query("SELECT * FROM users")
* })
*
* const provided = Effect.provideContext(program, context)
* ```
*
* @category environment
* @since 4.0.0
*/
export const provideContext = internal.provideContext;
/**
* Accesses a service from the context.
*
* **Example** (Accessing a required service)
*
* ```ts
* import { Context, Effect } from "effect"
*
* interface Database {
* readonly query: (sql: string) => Effect.Effect<string>
* }
*
* const Database = Context.Service<Database>("Database")
*
* const program = Effect.gen(function*() {
* const db = yield* Effect.service(Database)
* return yield* db.query("SELECT * FROM users")
* })
* ```
*
* @category context
* @since 4.0.0
*/
export const service = internal.service;
/**
* Optionally accesses a service from the environment.
*
* **When to use**
*
* Use to read an optional dependency from the current context without making
* that dependency part of the effect's required environment.
*
* **Details**
*
* This function attempts to access a service from the environment. If the
* service is available, it returns `Some(service)`. If the service is not
* available, it returns `None`. Unlike `service`, this function does not
* require the service to be present in the environment.
*
* **Example** (Accessing an optional service)
*
* ```ts
* import { Context, Effect, Option } from "effect"
*
* // Define a service key
* const Logger = Context.Service<{
* log: (msg: string) => void
* }>("Logger")
*
* // Use serviceOption to optionally access the logger
* const program = Effect.gen(function*() {
* const maybeLogger = yield* Effect.serviceOption(Logger)
*
* if (Option.isSome(maybeLogger)) {
* maybeLogger.value.log("Service is available")
* } else {
* console.log("Service not available")
* }
* })
* ```
*
* @category context
* @since 2.0.0
*/
export const serviceOption = internal.serviceOption;
/**
* Provides part of the required context while leaving the rest unchanged.
*
* **Details**
*
* This function allows you to transform the context required by an effect,
* providing part of the context and leaving the rest to be fulfilled later.
*
* **Example** (Updating the context before running)
*
* ```ts
* import { Context, Effect } from "effect"
*
* // Define services
* const Logger = Context.Service<{
* log: (msg: string) => void
* }>("Logger")
* const Config = Context.Service<{
* name: string
* }>("Config")
*
* const program = Effect.service(Config).pipe(
* Effect.map((config) => `Hello ${config.name}!`)
* )
*
* // Transform services by providing Config while keeping Logger requirement
* const configured = program.pipe(
* Effect.updateContext((context: Context.Context<typeof Logger>) =>
* Context.add(context, Config, { name: "World" })
* )
* )
*
* // The effect now requires only Logger service
* const result = Effect.provideService(configured, Logger, {
* log: (msg) => console.log(msg)
* })
* ```
*
* @category context
* @since 4.0.0
*/
export const updateContext = internal.updateContext;
/**
* Runs an effect with a service implementation transformed by the provided
* function.
*
* **Details**
*
* The service must be available in the effect's context; `updateService`
* replaces it for the wrapped effect with the value returned by the updater.
*
* **Example** (Replacing a service for one effect)
*
* ```ts
* import { Console, Context, Effect } from "effect"
*
* // Define a counter service
* const Counter = Context.Service<{ count: number }>("Counter")
*
* const program = Effect.gen(function*() {
* const updatedCounter = yield* Effect.service(Counter)
* yield* Console.log(`Updated count: ${updatedCounter.count}`)
* return updatedCounter.count
* }).pipe(
* Effect.updateService(Counter, (counter) => ({ count: counter.count + 1 }))
* )
*
* // Provide initial service and run
* const result = Effect.provideService(program, Counter, { count: 0 })
* Effect.runPromise(result).then(console.log)
* // Output: Updated count: 1
* // 1
* ```
*
* @category context
* @since 2.0.0
*/
export const updateService = internal.updateService;
/**
* Provides one concrete service implementation to an effect.
*
* **When to use**
*
* Use to satisfy one service requirement with an already-built implementation.
*
* **Details**
*
* The service requirement identified by the `Context.Key` is removed from the
* effect requirements after the implementation is provided.
*
* **Example** (Providing a service value)
*
* ```ts
* import { Console, Context, Effect } from "effect"
*
* // Define a service for configuration
* const Config = Context.Service<{
* apiUrl: string
* timeout: number
* }>("Config")
*
* const fetchData = Effect.gen(function*() {
* const config = yield* Effect.service(Config)
* yield* Console.log(`Fetching from: ${config.apiUrl}`)
* yield* Console.log(`Timeout: ${config.timeout}ms`)
* return "data"
* })
*
* // Provide the service implementation
* const program = Effect.provideService(fetchData, Config, {
* apiUrl: "https://api.example.com",
* timeout: 5000
* })
*
* Effect.runPromise(program).then(console.log)
* // Output:
* // Fetching from: https://api.example.com
* // Timeout: 5000ms
* // data
* ```
*
* @see {@link provide} for providing multiple layers to an effect.
* @see {@link provideServiceEffect} for acquiring the service implementation effectfully.
* @see {@link provideContext} for providing a complete context.
* @category context
* @since 2.0.0
*/
export const provideService = internal.provideService;
/**
* Provides one service to an effect using an effectful acquisition.
*
* **When to use**
*
* Use when the service implementation must be created by an effect and its
* acquisition failure should remain in the returned effect.
*
* **Details**
*
* `provideServiceEffect` runs the acquisition effect to produce the service
* implementation, removes that service from the wrapped effect's requirements,
* and leaves any other requirements to be provided later. Acquisition failures
* are included in the returned effect's error channel.
*
* **Example** (Providing a service with an effect)
*
* ```ts
* import { Console, Context, Effect } from "effect"
*
* // Define a database connection service
* interface DatabaseConnection {
* readonly query: (sql: string) => Effect.Effect<string>
* }
* const Database = Context.Service<DatabaseConnection>("Database")
*
* // Effect that creates a database connection
* const createConnection = Effect.gen(function*() {
* yield* Console.log("Establishing database connection...")
* yield* Effect.sleep("100 millis") // Simulate connection time
* yield* Console.log("Database connected!")
* return {
* query: (sql: string) => Effect.succeed(`Result for: ${sql}`)
* }
* })
*
* const program = Effect.gen(function*() {
* const db = yield* Effect.service(Database)
* return yield* db.query("SELECT * FROM users")
* })
*
* // Provide the service through an effect
* const withDatabase = Effect.provideServiceEffect(
* program,
* Database,
* createConnection
* )
*
* Effect.runPromise(withDatabase).then(console.log)
* // Output:
* // Establishing database connection...
* // Database connected!
* // Result for: SELECT * FROM users
* ```
*
* @category context
* @since 2.0.0
*/
export const provideServiceEffect = internal.provideServiceEffect;
// -----------------------------------------------------------------------------
// References
// -----------------------------------------------------------------------------
/**
* Sets the concurrency level for parallel operations within an effect.
*
* **Example** (Setting local concurrency)
*
* ```ts
* import { Console, Effect } from "effect"
*
* const task = (id: number) =>
* Effect.gen(function*() {
* yield* Console.log(`Task ${id} starting`)
* yield* Effect.sleep("100 millis")
* yield* Console.log(`Task ${id} completed`)
* return id
* })
*
* // Run tasks with limited concurrency (max 2 at a time)
* const program = Effect.gen(function*() {
* const tasks = [1, 2, 3, 4, 5].map(task)
* return yield* Effect.all(tasks, { concurrency: 2 })
* }).pipe(
* Effect.withConcurrency(2)
* )
*
* Effect.runPromise(program).then(console.log)
* // Tasks will run with max 2 concurrent operations
* // [1, 2, 3, 4, 5]
* ```
*
* @category references
* @since 2.0.0
*/
export const withConcurrency = internal.withConcurrency;
// -----------------------------------------------------------------------------
// Resource management & finalization
// -----------------------------------------------------------------------------
/**
* Returns the current scope for resource management.
*
* **Example** (Accessing the current scope)
*
* ```ts
* import { Console, Effect } from "effect"
*
* const program = Effect.gen(function*() {
* const currentScope = yield* Effect.scope
* yield* Console.log("Got scope for resource management")
*
* // Use the scope to manually manage resources if needed
* const resource = yield* Effect.acquireRelease(
* Console.log("Acquiring resource").pipe(Effect.as("resource")),
* () => Console.log("Releasing resource")
* )
*
* return resource
* })
*
* Effect.runPromise(Effect.scoped(program)).then(console.log)
* // Output:
* // Got scope for resource management
* // Acquiring resource
* // resource
* // Releasing resource
* ```
*
* @category resource management
* @since 2.0.0
*/
export const scope = internal.scope;
/**
* Runs an effect with a scope that closes when the effect completes.
*
* **When to use**
*
* Use to acquire scoped resources for the duration of a single workflow.
*
* **Details**
*
* Finalizers for resources acquired inside the workflow run as soon as the
* workflow completes, whether by success, failure, or interruption.
*
* **Example** (Running a scoped acquisition)
*
* ```ts
* import { Console, Effect } from "effect"
*
* const resource = Effect.acquireRelease(
* Console.log("Acquiring resource").pipe(Effect.as("resource")),
* () => Console.log("Releasing resource")
* )
*
* const program = Effect.scoped(
* Effect.gen(function*() {
* const res = yield* resource
* yield* Console.log(`Using ${res}`)
* return res
* })
* )
*
* Effect.runFork(program)
* // Output: "Acquiring resource"
* // Output: "Using resource"
* // Output: "Releasing resource"
* ```
*
* @category resource management
* @since 2.0.0
*/
export const scoped = internal.scoped;
/**
* Creates a scoped effect by providing access to the scope.
*
* **When to use**
*
* Use when resource acquisition needs direct access to the scope being created,
* for example to register finalizers manually.
*
* **Example** (Working with an explicit scope)
*
* ```ts
* import { Console, Effect, Scope } from "effect"
*
* const program = Effect.scopedWith((scope) =>
* Effect.gen(function*() {
* yield* Console.log("Inside scoped context")
*
* // Manually add a finalizer to the scope
* yield* Scope.addFinalizer(scope, Console.log("Manual finalizer"))
*
* // Create a scoped resource
* const resource = yield* Effect.scoped(
* Effect.acquireRelease(
* Console.log("Acquiring resource").pipe(Effect.as("resource")),
* () => Console.log("Releasing resource")
* )
* )
*
* return resource
* })
* )
*
* Effect.runPromise(program).then(console.log)
* // Output:
* // Inside scoped context
* // Acquiring resource
* // resource
* // Releasing resource
* // Manual finalizer
* ```
*
* @category resource management
* @since 3.11.0
*/
export const scopedWith = internal.scopedWith;
/**
* Constructs a scoped resource from an acquisition effect and a release
* finalizer.
*
* **When to use**
*
* Use to acquire a scoped resource with an explicit release finalizer.
*
* **Details**
*
* If acquisition succeeds, the release finalizer is added to the current scope
* and is guaranteed to run when that scope closes. The finalizer receives the
* `Exit` value used to close the scope.
*
* By default, acquisition is protected by an uninterruptible region. Pass
* `{ interruptible: true }` to allow the acquisition effect to be interrupted.
*
* **Example** (Acquiring and releasing a resource)
*
* ```ts
* import { Console, Effect, Exit } from "effect"
*
* // Simulate a resource that needs cleanup
* interface FileHandle {
* readonly path: string
* readonly content: string
* }
*
* // Acquire a file handle
* const acquire = Effect.gen(function*() {
* yield* Console.log("Opening file")
* return { path: "/tmp/file.txt", content: "file content" }
* })
*
* // Release the file handle
* const release = (handle: FileHandle, exit: Exit.Exit<unknown, unknown>) =>
* Console.log(
* `Closing file ${handle.path} with exit: ${
* Exit.isSuccess(exit) ? "success" : "failure"
* }`
* )
*
* // Create a scoped resource
* const resource = Effect.acquireRelease(acquire, release)
*
* // Use the resource within a scope
* const program = Effect.scoped(
* Effect.gen(function*() {
* const handle = yield* resource
* yield* Console.log(`Using file: ${handle.path}`)
* return handle.content
* })
* )
* ```
*
* @see {@link acquireDisposable} for resources that implement JavaScript disposal protocols
* @see {@link acquireUseRelease} for bracketing acquire, use, and release in one effect
*
* @category resource management
* @since 2.0.0
*/
export const acquireRelease = internal.acquireRelease;
/**
* Acquires a scoped resource that implements JavaScript disposal protocols.
*
* **When to use**
*
* Use when you work with JavaScript `Disposable` or `AsyncDisposable` resources
* that should be closed with the surrounding scope.
*
* **Details**
*
* The resource is automatically disposed when the surrounding
* {@link Scope} is closed, using {@link Symbol.dispose} for
* synchronous disposables or {@link Symbol.asyncDispose} for asynchronous
* disposables.
*
* This is similar to {@link acquireRelease}, but uses the standard
* JavaScript disposal protocol instead of requiring an explicit release
* function. It works with JavaScript `Disposable` and `AsyncDisposable`
* resources.
*
* **Example** (Acquiring a disposable resource)
*
* ```ts
* import sqlite from "node:sqlite";
* import { Effect } from "effect"
*
* const program = Effect.scoped(
* Effect.gen(function* () {
* // acquire database connection
* // database will be closed when the scope is closed
* const db = yield* Effect.acquireDisposable(
* Effect.sync(() => new sqlite.DatabaseSync(":memory:"))
* )
*
* const row = db.prepare("SELECT 1 AS value").get()
* yield* Effect.log(row) // { value: 1 }
* })
* )
* ```
*
* @see {@link acquireRelease} for resources that need an explicit finalizer
*
* @category resource management
* @since 4.0.0
*/
export const acquireDisposable = internal.acquireDisposable;
/**
* Runs resource acquisition, usage, and release as one bracketed effect.
*
* **When to use**
*
* Use to bracket acquire, use, and release logic in one effect.
*
* **Details**
*
* `acquireUseRelease` does the following:
*
* 1. Ensures that the `Effect` value that acquires the resource will not be
* interrupted. Note that acquisition may still fail due to internal
* reasons (such as an uncaught exception).
* 2. Ensures that the `release` `Effect` value will not be interrupted,
* and will be executed as long as the acquisition `Effect` value
* successfully acquires the resource.
*
* During the time period between the acquisition and release of the resource,
* the `use` `Effect` value will be executed.
*
* If the `release` `Effect` value fails, then the entire `Effect` value will
* fail, even if the `use` `Effect` value succeeds. If this fail-fast behavior
* is not desired, errors produced by the `release` `Effect` value can be caught
* and ignored.
*
* **Example** (Using a resource with cleanup)
*
* ```ts
* import { Console, Effect, Exit } from "effect"
*
* interface Database {
* readonly connection: string
* readonly query: (sql: string) => Effect.Effect<string>
* }
*
* const program = Effect.acquireUseRelease(
* // Acquire - connect to database
* Effect.gen(function*() {
* yield* Console.log("Connecting to database...")
* return {
* connection: "db://localhost:5432",
* query: (sql: string) => Effect.succeed(`Result for: ${sql}`)
* }
* }),
* // Use - perform database operations
* (db) =>
* Effect.gen(function*() {
* yield* Console.log(`Connected to ${db.connection}`)
* const result = yield* db.query("SELECT * FROM users")
* yield* Console.log(`Query result: ${result}`)
* return result
* }),
* // Release - close database connection
* (db, exit) =>
* Effect.gen(function*() {
* if (Exit.isSuccess(exit)) {
* yield* Console.log(`Closing connection to ${db.connection} (success)`)
* } else {
* yield* Console.log(`Closing connection to ${db.connection} (failure)`)
* }
* })
* )
*
* Effect.runPromise(program)
* // Output:
* // Connecting to database...
* // Connected to db://localhost:5432
* // Query result: Result for: SELECT * FROM users
* // Closing connection to db://localhost:5432 (success)
* ```
*
* @see {@link acquireRelease} for scoped resources whose use happens later
*
* @category resource management
* @since 2.0.0
*/
export const acquireUseRelease = internal.acquireUseRelease;
/**
* Adds a finalizer to the current scope.
*
* **When to use**
*
* Use to register low-level cleanup in the current scope.
*
* **Details**
*
* The finalizer runs when the surrounding scope is closed and receives the
* `Exit` value used to close the scope.
*
* **Example** (Registering scope finalizers)
*
* ```ts
* import { Console, Effect, Exit } from "effect"
*
* const program = Effect.scoped(
* Effect.gen(function*() {
* // Add a finalizer that runs when the scope closes
* yield* Effect.addFinalizer((exit) =>
* Console.log(
* Exit.isSuccess(exit)
* ? "Cleanup: Operation completed successfully"
* : "Cleanup: Operation failed, cleaning up resources"
* )
* )
*
* yield* Console.log("Performing main operation...")
*
* // This could succeed or fail
* return "operation result"
* })
* )
*
* Effect.runPromise(program).then(console.log)
* // Output:
* // Performing main operation...
* // Cleanup: Operation completed successfully
* // operation result
* ```
*
* @see {@link acquireRelease} for resource acquisition with a release finalizer
* @see {@link ensuring} for attaching a finalizer to one effect
*
* @category resource management
* @since 2.0.0
*/
export const addFinalizer = internal.addFinalizer;
/**
* Returns an effect that, if this effect _starts_ execution, then the
* specified `finalizer` is guaranteed to be executed, whether this effect
* succeeds, fails, or is interrupted.
*
* **Details**
*
* For use cases that need access to the effect's result, see `onExit`.
*
* Finalizers offer very powerful guarantees, but they are low-level, and
* should generally not be used for releasing resources. For higher-level
* logic built on `ensuring`, see the `acquireRelease` family of methods.
*
* **Example** (Always running cleanup)
*
* ```ts
* import { Console, Effect } from "effect"
*
* const task = Effect.gen(function*() {
* yield* Console.log("Task started")
* yield* Effect.sleep("1 second")
* yield* Console.log("Task completed")
* return 42
* })
*
* // Ensure cleanup always runs, regardless of success or failure
* const program = Effect.ensuring(
* task,
* Console.log("Cleanup: This always runs!")
* )
*
* Effect.runPromise(program).then(console.log)
* // Output:
* // Task started
* // Task completed
* // Cleanup: This always runs!
* // 42
* ```
*
* @category resource management
* @since 2.0.0
*/
export const ensuring = internal.ensuring;
/**
* Runs the specified effect if this effect fails, providing the error to the
* effect if it exists. The provided effect will not be interrupted.
*
* **Example** (Running cleanup on failure)
*
* ```ts
* import { Cause, Console, Data, Effect } from "effect"
*
* class TaskError extends Data.TaggedError("TaskError")<{ readonly message: string }> {}
*
* const task = Effect.fail(new TaskError({ message: "Something went wrong" }))
*
* const program = Effect.onError(
* task,
* (cause) => Console.log(`Cleanup on error: ${Cause.squash(cause)}`)
* )
*
* Effect.runPromise(program).catch(console.error)
* // Output:
* // Cleanup on error: TaskError: Something went wrong
* // TaskError: Something went wrong
* ```
*
* @category resource management
* @since 2.0.0
*/
export const onError = internal.onError;
/**
* Runs the finalizer only when this effect fails and the `Cause` matches the
* provided predicate.
*
* **Example** (Running cleanup for selected failures)
*
* ```ts
* import { Cause, Console, Effect } from "effect"
*
* const task = Effect.fail("boom")
*
* const program = Effect.onErrorIf(
* task,
* Cause.hasFails,
* (cause) =>
* Effect.gen(function*() {
* yield* Console.log(`Cause: ${Cause.pretty(cause)}`)
* })
* )
* ```
*
* @category resource management
* @since 4.0.0
*/
export const onErrorIf = internal.onErrorIf;
/**
* Runs the finalizer only when this effect fails and the cause matches the provided `Filter`.
*
* **When to use**
*
* Use when cleanup or diagnostics should run only for failures whose full
* `Cause` is accepted or transformed by a `Filter`, and the finalizer needs the
* filter's pass value plus the original cause.
*
* @see {@link onError} for cleanup on every failure
* @see {@link onErrorIf} for selecting failures with a boolean predicate
* @see {@link onExitFilter} for selecting from every exit instead of only failures
*
* @category resource management
* @since 4.0.0
*/
export const onErrorFilter = internal.onErrorFilter;
/**
* Runs an optional finalizer with the effect's `Exit` value when the effect
* completes.
*
* **When to use**
*
* Use when you are building a low-level `Effect` operator that must inspect the
* source effect's `Exit`, may skip finalization by returning `undefined`, or
* must choose whether finalization is forced into an uninterruptible region.
*
* **Details**
*
* This low-level operator preserves the source effect's result unless the
* finalizer fails. Prefer `onExit` for normal cleanup logic.
*
* @see {@link onExit} for ordinary exit-aware cleanup whose finalizer always returns an effect
*
* @category resource management
* @since 4.0.0
*/
export const onExitPrimitive = internal.onExitPrimitive;
/**
* Ensures that a cleanup function runs whether this effect succeeds, fails, or
* is interrupted.
*
* **Example** (Observing every exit)
*
* ```ts
* import { Console, Effect, Exit } from "effect"
*
* const task = Effect.succeed(42)
*
* const program = Effect.onExit(task, (exit) =>
* Console.log(
* Exit.isSuccess(exit)
* ? `Task succeeded with: ${exit.value}`
* : `Task failed: ${Exit.isFailure(exit) ? exit.cause : "interrupted"}`
* ))
*
* Effect.runPromise(program).then(console.log)
* // Output:
* // Task succeeded with: 42
* // 42
* ```
*
* @category resource management
* @since 2.0.0
*/
export const onExit = internal.onExit;
/**
* Runs the cleanup effect only when the `Exit` satisfies the provided
* predicate.
*
* **Example** (Observing selected exits)
*
* ```ts
* import { Console, Effect, Exit } from "effect"
*
* const program = Effect.onExitIf(
* Effect.succeed(42),
* Exit.isSuccess,
* (exit) =>
* Exit.isSuccess(exit)
* ? Console.log(`Succeeded with: ${exit.value}`)
* : Effect.void
* )
* ```
*
* @category resource management
* @since 4.0.0
*/
export const onExitIf = internal.onExitIf;
/**
* Runs the cleanup effect only when the `Exit` matches the provided `Filter`.
*
* **When to use**
*
* Use when cleanup should run only for `Exit` values selected by a `Filter`,
* and the cleanup needs the extracted pass value together with the original
* `Exit`.
*
* **Details**
*
* `Result.fail` skips cleanup, and `Result.succeed` runs cleanup with the
* selected value and the original `Exit`.
*
* @see {@link onExit} for cleanup on every exit
* @see {@link onExitIf} for selecting exits with a boolean predicate
* @see {@link onErrorFilter} for selecting only failure causes
*
* @category resource management
* @since 4.0.0
*/
export const onExitFilter = internal.onExitFilter;
// -----------------------------------------------------------------------------
// Caching
// -----------------------------------------------------------------------------
/**
* Returns an effect that lazily computes a result and caches it for subsequent
* evaluations.
*
* **When to use**
*
* Use when you need an expensive or time-consuming operation to be evaluated
* once and reused by later callers.
*
* **Details**
*
* This function wraps an effect and ensures that its result is computed only
* once. Once the result is computed, it is cached, meaning that subsequent
* evaluations of the same effect will return the cached result without
* re-executing the logic.
*
* **Example** (Memoizing an effect until invalidated)
*
* ```ts
* import { Console, Effect } from "effect"
*
* let i = 1
* const expensiveTask = Effect.promise<string>(() => {
* console.log("expensive task...")
* return new Promise((resolve) => {
* setTimeout(() => {
* resolve(`result ${i++}`)
* }, 100)
* })
* })
*
* const program = Effect.gen(function*() {
* console.log("non-cached version:")
* yield* expensiveTask.pipe(Effect.andThen(Console.log))
* yield* expensiveTask.pipe(Effect.andThen(Console.log))
* console.log("cached version:")
* const cached = yield* Effect.cached(expensiveTask)
* yield* cached.pipe(Effect.andThen(Console.log))
* yield* cached.pipe(Effect.andThen(Console.log))
* })
*
* Effect.runFork(program)
* // Output:
* // non-cached version:
* // expensive task...
* // result 1
* // expensive task...
* // result 2
* // cached version:
* // expensive task...
* // result 3
* // result 3
* ```
*
* @see {@link cachedWithTTL} for a similar function that includes a
* time-to-live duration for the cached value.
* @see {@link cachedInvalidateWithTTL} for a similar function that includes an
* additional effect for manually invalidating the cached value.
* @category caching
* @since 2.0.0
*/
export const cached = internal.cached;
/**
* Returns an effect that caches its result for a specified `Duration`,
* known as "timeToLive" (TTL).
*
* **When to use**
*
* Use when you need a costly effect result to be reused for a bounded duration
* before being recomputed.
*
* **Details**
*
* This function is used to cache the result of an effect for a specified amount
* of time. This means that the first time the effect is evaluated, its result
* is computed and stored.
*
* If the effect is evaluated again within the specified `timeToLive`, the
* cached result will be used, avoiding recomputation.
*
* After the specified duration has passed, the cache expires, and the effect
* will be recomputed upon the next evaluation.
*
* **Example** (Memoizing an effect with TTL)
*
* ```ts
* import { Console, Effect } from "effect"
*
* let i = 1
* const expensiveTask = Effect.promise<string>(() => {
* console.log("expensive task...")
* return new Promise((resolve) => {
* setTimeout(() => {
* resolve(`result ${i++}`)
* }, 100)
* })
* })
*
* const program = Effect.gen(function*() {
* const cached = yield* Effect.cachedWithTTL(expensiveTask, "150 millis")
* yield* cached.pipe(Effect.andThen(Console.log))
* yield* cached.pipe(Effect.andThen(Console.log))
* yield* Effect.sleep("100 millis")
* yield* cached.pipe(Effect.andThen(Console.log))
* })
*
* Effect.runFork(program)
* // Output:
* // expensive task...
* // result 1
* // result 1
* // expensive task...
* // result 2
* ```
*
* @see {@link cached} for a similar function that caches the result
* indefinitely.
* @see {@link cachedInvalidateWithTTL} for a similar function that includes an
* additional effect for manually invalidating the cached value.
* @category caching
* @since 2.0.0
*/
export const cachedWithTTL = internal.cachedWithTTL;
/**
* Creates a cached effect result for a specified duration and allows manual
* invalidation before expiration.
*
* **When to use**
*
* Use when an effect result should be cached for a bounded time and callers
* also need a manual invalidation effect to force recomputation before
* expiration.
*
* **Details**
*
* This function behaves similarly to {@link cachedWithTTL} by caching the
* result of an effect for a specified period of time. However, it introduces an
* additional feature: it provides an effect that allows you to manually
* invalidate the cached result before it naturally expires.
*
* This gives you more control over the cache, allowing you to refresh the
* result when needed, even if the original cache has not yet expired.
*
* Once the cache is invalidated, the next time the effect is evaluated, the
* result will be recomputed, and the cache will be refreshed.
*
* **Example** (Memoizing with TTL and invalidation)
*
* ```ts
* import { Console, Effect } from "effect"
*
* let i = 1
* const expensiveTask = Effect.promise<string>(() => {
* console.log("expensive task...")
* return new Promise((resolve) => {
* setTimeout(() => {
* resolve(`result ${i++}`)
* }, 100)
* })
* })
*
* const program = Effect.gen(function*() {
* const [cached, invalidate] = yield* Effect.cachedInvalidateWithTTL(
* expensiveTask,
* "1 hour"
* )
* yield* cached.pipe(Effect.andThen(Console.log))
* yield* cached.pipe(Effect.andThen(Console.log))
* yield* invalidate
* yield* cached.pipe(Effect.andThen(Console.log))
* })
*
* Effect.runFork(program)
* // Output:
* // expensive task...
* // result 1
* // result 1
* // expensive task...
* // result 2
* ```
*
* @see {@link cached} for a similar function that caches the result
* indefinitely.
* @see {@link cachedWithTTL} for a similar function that caches the result for
* a specified duration but does not include an effect for manual invalidation.
* @category caching
* @since 2.0.0
*/
export const cachedInvalidateWithTTL = internal.cachedInvalidateWithTTL;
// -----------------------------------------------------------------------------
// Interruption
// -----------------------------------------------------------------------------
/**
* Returns an effect that is immediately interrupted.
*
* **Example** (Creating an interrupted effect)
*
* ```ts
* import { Effect } from "effect"
*
* const program = Effect.gen(function*() {
* return yield* Effect.interrupt
* yield* Effect.succeed("This won't execute and is unreachable")
* })
*
* Effect.runPromise(program).catch(console.error)
* // Throws: InterruptedException
* ```
*
* @category interruption
* @since 2.0.0
*/
export const interrupt = internal.interrupt;
/**
* Returns a new effect that allows the effect to be interruptible.
*
* **Example** (Allowing interruption)
*
* ```ts
* import { Effect } from "effect"
*
* const longRunning = Effect.forever(Effect.succeed("working..."))
*
* const program = Effect.interruptible(longRunning)
*
* // This effect can now be interrupted
* const fiber = Effect.runFork(program)
* // Later: fiber.interrupt()
* ```
*
* @category interruption
* @since 2.0.0
*/
export const interruptible = internal.interruptible;
/**
* Runs the specified finalizer effect if this effect is interrupted.
*
* **Example** (Running cleanup on interruption)
*
* ```ts
* import { Console, Effect, Fiber } from "effect"
*
* const task = Effect.forever(Effect.succeed("working..."))
*
* const program = Effect.onInterrupt(
* task,
* () => Console.log("Task was interrupted, cleaning up...")
* )
*
* const fiber = Effect.runFork(program)
* // Later interrupt the task
* Effect.runFork(Fiber.interrupt(fiber))
* // Output: Task was interrupted, cleaning up...
* ```
*
* @category interruption
* @since 2.0.0
*/
export const onInterrupt = internal.onInterrupt;
/**
* Returns a new effect that disables interruption for the given effect.
*
* **Example** (Preventing interruption)
*
* ```ts
* import { Console, Effect, Fiber } from "effect"
*
* const criticalTask = Effect.gen(function*() {
* yield* Console.log("Starting critical section...")
* yield* Effect.sleep("2 seconds")
* yield* Console.log("Critical section completed")
* })
*
* const program = Effect.uninterruptible(criticalTask)
*
* const fiber = Effect.runFork(program)
* // Even if interrupted, the critical task will complete
* Effect.runPromise(Fiber.interrupt(fiber))
* ```
*
* @category interruption
* @since 2.0.0
*/
export const uninterruptible = internal.uninterruptible;
/**
* Disables interruption and provides a restore function to restore the
* interruptible state within the effect.
*
* **Example** (Restoring interruption in protected regions)
*
* ```ts
* import { Console, Effect } from "effect"
*
* const program = Effect.uninterruptibleMask((restore) =>
* Effect.gen(function*() {
* yield* Console.log("Uninterruptible phase...")
* yield* Effect.sleep("1 second")
*
* // Restore interruptibility for this part
* yield* restore(
* Effect.gen(function*() {
* yield* Console.log("Interruptible phase...")
* yield* Effect.sleep("2 seconds")
* })
* )
*
* yield* Console.log("Back to uninterruptible")
* })
* )
* ```
*
* @category interruption
* @since 2.0.0
*/
export const uninterruptibleMask = internal.uninterruptibleMask;
/**
* Runs an effect in an interruptible region while providing `restore` for
* locally restoring the previous interruptibility.
*
* **Example** (Controlling interruptibility locally)
*
* ```ts
* import { Console, Effect } from "effect"
*
* const program = Effect.interruptibleMask((restore) =>
* Effect.gen(function*() {
* yield* Console.log("Interruptible phase...")
* yield* Effect.sleep("1 second")
*
* // Make this part uninterruptible
* yield* restore(
* Effect.gen(function*() {
* yield* Console.log("Uninterruptible phase...")
* yield* Effect.sleep("2 seconds")
* })
* )
*
* yield* Console.log("Back to interruptible")
* })
* )
* ```
*
* @category interruption
* @since 2.0.0
*/
export const interruptibleMask = internal.interruptibleMask;
/**
* Creates an AbortSignal that is managed by the provided scope.
*
* **When to use**
*
* Use to obtain a scope-managed `AbortSignal` for APIs that accept cancellation
* through a signal.
*
* **Details**
*
* Each acquisition creates a fresh `AbortController`. Closing the owning scope
* runs a finalizer that aborts the controller and the effect succeeds with the
* controller's signal.
*
* **Gotchas**
*
* The signal is aborted when its owning scope closes, so avoid keeping it for
* work that outlives that scope.
*
* @see {@link scoped} for binding resource lifetime to a scope
*
* @category interruption
* @since 4.0.0
*/
export const abortSignal = internal.abortSignal;
/**
* Repeats this effect forever (until the first error).
*
* **Example** (Repeating forever)
*
* ```ts
* import { Console, Effect, Fiber } from "effect"
*
* const task = Effect.gen(function*() {
* yield* Console.log("Task running...")
* yield* Effect.sleep("1 second")
* })
*
* // This will run forever, printing every second
* const program = task.pipe(Effect.forever)
*
* // This will run forever, without yielding every iteration
* const programNoYield = task.pipe(Effect.forever({ disableYield: true }))
*
* // Run for 5 seconds then interrupt
* const timedProgram = Effect.gen(function*() {
* const fiber = yield* Effect.forkChild(program)
* yield* Effect.sleep("5 seconds")
* yield* Fiber.interrupt(fiber)
* })
* ```
*
* @category repetition
* @since 2.0.0
*/
export const forever = internal.forever;
/**
* Repeats an effect based on a specified schedule or until the first failure.
*
* **When to use**
*
* Use to rerun an effect after successful executions.
*
* **Details**
*
* This function executes an effect repeatedly according to the given schedule.
* Each repetition occurs after the initial execution of the effect, meaning
* that the schedule determines the number of additional repetitions. For
* example, using `Schedule.once` will result in the effect being executed twice
* (once initially and once as part of the repetition).
*
* If the effect succeeds, it is repeated according to the schedule. If it
* fails, the repetition stops immediately, and the failure is returned.
*
* The schedule can also specify delays between repetitions, making it useful
* for tasks like retrying operations with backoff, periodic execution, or
* performing a series of dependent actions.
*
* You can combine schedules for more advanced repetition logic, such as adding
* delays, limiting recursions, or dynamically adjusting based on the outcome of
* each execution.
*
* **Gotchas**
*
* The source effect is always evaluated once before the schedule is stepped.
* The schedule controls additional repetitions, not the initial execution.
*
* **Example** (Repeating successful effects with a schedule)
*
* ```ts
* // Success Example
* import { Console, Effect, Schedule } from "effect"
*
* const action = Console.log("success")
* const policy = Schedule.addDelay(Schedule.recurs(2), () => Effect.succeed("100 millis"))
* const program = Effect.repeat(action, policy)
*
* // Effect.runPromise(program).then((n) => console.log(`repetitions: ${n}`))
* ```
*
* **Example** (Stopping repetition on failure)
*
* ```ts
* // Failure Example
* import { Effect, Schedule } from "effect"
*
* let count = 0
*
* // Define a callback effect that simulates an action with possible failures
* const action = Effect.callback<string, string>((resume) => {
* if (count > 1) {
* console.log("failure")
* resume(Effect.fail("Uh oh!"))
* } else {
* count++
* console.log("success")
* resume(Effect.succeed("yay!"))
* }
* })
*
* const policy = Schedule.addDelay(Schedule.recurs(2), () => Effect.succeed("100 millis"))
* const program = Effect.repeat(action, policy)
*
* // Effect.runPromiseExit(program).then(console.log)
* ```
*
* @see {@link retry} for failure-based repetition
* @see {@link repeatOrElse} for fallback handling when repetition fails
*
* @category repetition
* @since 2.0.0
*/
export const repeat = internalSchedule.repeat;
/**
* Repeats an effect according to a schedule and runs a fallback effect if
* repetition fails before the schedule completes.
*
* **When to use**
*
* Use when successful repetitions should follow a schedule, but failures from
* the repeated effect or schedule need an effectful fallback.
*
* **Details**
*
* If the repeated effect or schedule step fails, `orElse` receives the failure
* and the latest schedule metadata when at least one schedule step has run;
* otherwise it receives `None`. If the schedule completes normally, the
* returned effect succeeds with the schedule's output.
*
* **Example** (Recovering after repetition stops)
*
* ```ts
* import { Console, Effect, Option, Schedule } from "effect"
*
* let attempt = 0
* const task = Effect.gen(function*() {
* attempt++
* if (attempt <= 2) {
* yield* Console.log(`Attempt ${attempt} failed`)
* return yield* Effect.fail(`Error ${attempt}`)
* }
* yield* Console.log(`Attempt ${attempt} succeeded`)
* return "success"
* })
*
* const program = Effect.repeatOrElse(
* task,
* Schedule.recurs(3),
* (error, attempts) =>
* Console.log(
* `Final failure: ${error}, after ${
* Option.getOrElse(attempts, () => 0)
* } attempts`
* ).pipe(Effect.map(() => 0))
* )
* ```
*
* @category repetition
* @since 2.0.0
*/
export const repeatOrElse = internalSchedule.repeatOrElse;
/**
* Returns an array of `n` identical effects.
*
* **When to use**
*
* Use when you need an array of identical effect values without running them
* yet.
*
* **Details**
*
* This only creates the array of effects. It does not run or collect them.
*
* @see {@link all} for running the returned effects and collecting results
* @see {@link replicateEffect} for repeating an effect and collecting results in one step with concurrency and discard options
*
* @category collecting
* @since 2.0.0
*/
export const replicate = internal.replicate;
/**
* Performs this effect `n` times and collects results with `Effect.all` semantics.
*
* **When to use**
*
* Use when you want to run the repeated effects immediately, with optional
* concurrency control or result discarding.
*
* **Details**
*
* Use `concurrency` to control parallelism and `discard: true` to ignore results.
*
* **Example** (Replicating an effect)
*
* ```ts
* import { Console, Effect } from "effect"
*
* const program = Effect.gen(function*() {
* const results = yield* Effect.replicateEffect(3)(Effect.succeed(1))
* yield* Console.log(results)
* })
* ```
*
* @category collecting
* @since 2.0.0
*/
export const replicateEffect = internal.replicateEffect;
/**
* Runs an effect repeatedly according to a schedule and returns the schedule's
* final output.
*
* **When to use**
*
* Use to rerun a successful effect according to a `Schedule` when the schedule
* does not need a custom initial input.
*
* **Details**
*
* The schedule is first stepped with `undefined`. After each successful
* execution, the effect's success value is fed to the schedule to decide
* whether to run again. The returned effect fails if the effect or schedule
* fails, and otherwise succeeds with the schedule output when the schedule
* completes.
*
* **Example** (Scheduling repeated execution)
*
* ```ts
* import { Console, Effect, Schedule } from "effect"
*
* const task = Effect.gen(function*() {
* yield* Console.log("Task executing...")
* return Math.random()
* })
*
* // Repeat 3 times with 1 second delay between executions
* const program = Effect.schedule(
* task,
* Schedule.addDelay(Schedule.recurs(2), () => Effect.succeed("1 second"))
* )
*
* Effect.runPromise(program).then(console.log)
* // Output:
* // Task executing... (immediate)
* // Task executing... (after 1 second)
* // Task executing... (after 1 second)
* // Returns the count from Schedule.recurs
* ```
*
* @see {@link scheduleFrom} for a variant that allows the schedule's decision
* to depend on the result of this effect.
*
* @category repetition
* @since 2.0.0
*/
export const schedule = /*#__PURE__*/dual(2, (self, schedule) => scheduleFrom(self, undefined, schedule));
/**
* Runs an effect repeatedly according to a schedule that is initialized with a
* specific schedule input.
*
* **Details**
*
* `initial` is passed to the schedule before the first execution, not to the
* effect itself. After each successful execution, the effect's success value is
* fed back into the schedule to decide whether to continue. The returned effect
* succeeds with the schedule output when the schedule completes and fails if
* the effect or schedule fails.
*
* **Example** (Scheduling from an initial value)
*
* ```ts
* import { Console, Effect, Schedule } from "effect"
*
* const task = (input: number) =>
* Effect.gen(function*() {
* yield* Console.log(`Processing: ${input}`)
* return input + 1
* })
*
* // Start with 0, repeat 3 times
* const program = Effect.scheduleFrom(
* task(0),
* 0,
* Schedule.recurs(2)
* )
*
* Effect.runPromise(program).then(console.log)
* // Returns the schedule count
* ```
*
* @category repetition
* @since 2.0.0
*/
export const scheduleFrom = internalSchedule.scheduleFrom;
// -----------------------------------------------------------------------------
// Tracing
// -----------------------------------------------------------------------------
/**
* Returns the current tracer from the context.
*
* **Example** (Accessing the current tracer)
*
* ```ts
* import { Effect } from "effect"
*
* const program = Effect.gen(function*() {
* const currentTracer = yield* Effect.tracer
* yield* Effect.log(`Using tracer: ${currentTracer}`)
* return "operation completed"
* })
* ```
*
* @category tracing
* @since 2.0.0
*/
export const tracer = internal.tracer;
/**
* Provides a tracer to an effect.
*
* **Example** (Providing a tracer)
*
* ```ts
* import { Effect } from "effect"
*
* const program = Effect.gen(function*() {
* yield* Effect.log("Using tracer")
* return "completed"
* })
*
* // withTracer provides a tracer to the effect context
* // const traced = Effect.withTracer(program, customTracer)
* ```
*
* @category tracing
* @since 2.0.0
*/
export const withTracer = internal.withTracer;
/**
* Enables or disables tracing for spans created by the given effect.
*
* **Details**
*
* When `enabled` is `false`, spans created inside the effect are not registered
* with the current tracer and do not propagate as normal trace parents.
*
* **Example** (Enabling or disabling tracing)
*
* ```ts
* import { Effect } from "effect"
*
* Effect.succeed(42).pipe(
* Effect.withSpan("my-span"),
* // the span will not be registered with the tracer
* Effect.withTracerEnabled(false)
* )
* ```
*
* @category tracing
* @since 2.0.0
*/
export const withTracerEnabled = internal.withTracerEnabled;
/**
* Enables or disables tracer timing for the given Effect.
*
* **Example** (Enabling or disabling tracing timing)
*
* ```ts
* import { Effect } from "effect"
*
* Effect.succeed(42).pipe(
* Effect.withSpan("my-span"),
* // the span will not have timing information
* Effect.withTracerTiming(false)
* )
* ```
*
* @category tracing
* @since 2.0.0
*/
export const withTracerTiming = internal.withTracerTiming;
/**
* Adds an annotation to each span in this effect.
*
* **Example** (Annotating all spans)
*
* ```ts
* import { Effect } from "effect"
*
* const program = Effect.gen(function*() {
* yield* Effect.log("Doing some work...")
* return "result"
* })
*
* // Add single annotation
* const annotated1 = Effect.annotateSpans(program, "user", "john")
*
* // Add multiple annotations
* const annotated2 = Effect.annotateSpans(program, {
* operation: "data-processing",
* version: "1.0.0",
* environment: "production"
* })
* ```
*
* @category tracing
* @since 2.0.0
*/
export const annotateSpans = internal.annotateSpans;
/**
* Adds an annotation to the current span if available.
*
* **Example** (Annotating the current span)
*
* ```ts
* import { Effect } from "effect"
*
* const program = Effect.gen(function*() {
* yield* Effect.annotateCurrentSpan("userId", "123")
* yield* Effect.annotateCurrentSpan({
* operation: "user-lookup",
* timestamp: Date.now()
* })
* yield* Effect.log("User lookup completed")
* return "success"
* })
*
* const traced = Effect.withSpan(program, "user-operation")
* ```
*
* @category tracing
* @since 2.0.0
*/
export const annotateCurrentSpan = internal.annotateCurrentSpan;
/**
* Returns the currently active local tracing span.
*
* **Details**
*
* The effect fails with `NoSuchElementError` when there is no active local
* `Span`.
*
* **Example** (Reading the current span)
*
* ```ts
* import { Effect } from "effect"
*
* const program = Effect.gen(function*() {
* const span = yield* Effect.currentSpan
* yield* Effect.log(`Current span: ${span}`)
* return "done"
* })
*
* const traced = Effect.withSpan(program, "my-span")
* ```
*
* @category tracing
* @since 2.0.0
*/
export const currentSpan = internal.currentSpan;
/**
* Returns the current parent span from the effect context.
*
* **Details**
*
* The effect succeeds with either a local span or external span when one is
* present, and fails with `NoSuchElementError` when no parent span is
* available.
*
* **Example** (Reading the parent span)
*
* ```ts
* import { Effect } from "effect"
*
* const childOperation = Effect.gen(function*() {
* const parentSpan = yield* Effect.currentParentSpan
* yield* Effect.log(`Parent span: ${parentSpan}`)
* return "child completed"
* })
*
* const program = Effect.gen(function*() {
* yield* Effect.withSpan(childOperation, "child-span")
* return "parent completed"
* })
*
* const traced = Effect.withSpan(program, "parent-span")
* ```
*
* @category tracing
* @since 2.0.0
*/
export const currentParentSpan = internal.currentParentSpan;
/**
* Returns the tracing span annotations currently carried in the effect context.
*
* **Details**
*
* These annotations are applied to spans created inside the context, such as
* spans created by `withSpan`, `useSpan`, or `makeSpan`.
*
* **Example** (Providing span annotations)
*
* ```ts
* import { Effect } from "effect"
*
* const program = Effect.gen(function*() {
* // Add some annotations to the current span
* yield* Effect.annotateCurrentSpan("userId", "123")
* yield* Effect.annotateCurrentSpan("operation", "data-processing")
*
* // Retrieve all annotations
* const annotations = yield* Effect.spanAnnotations
*
* console.log("Current span annotations:", annotations)
* return annotations
* })
*
* Effect.runPromise(program).then(console.log)
* // Output: Current span annotations: { userId: "123", operation: "data-processing" }
* ```
*
* @category tracing
* @since 2.0.0
*/
export const spanAnnotations = internal.spanAnnotations;
/**
* Returns the tracing span links currently carried in the effect context.
*
* **Details**
*
* These links are attached to spans created inside the context. Span links
* connect related spans without making one span the parent of another.
*
* **Example** (Providing span links)
*
* ```ts
* import { Effect } from "effect"
*
* const program = Effect.gen(function*() {
* // Get the current span links
* const links = yield* Effect.spanLinks
* console.log(`Current span has ${links.length} links`)
* return links
* })
* ```
*
* @category tracing
* @since 2.0.0
*/
export const spanLinks = internal.spanLinks;
/**
* Adds a link with the provided span to all spans in this effect.
*
* **Details**
*
* This is useful for connecting spans that are related but not in a direct
* parent-child relationship. For example, you might want to link spans from
* parallel operations or connect spans across different traces.
*
* **Example** (Linking one span to another span)
*
* ```ts
* import { Effect } from "effect"
*
* const parentEffect = Effect.withSpan("parent-operation")(
* Effect.succeed("parent result")
* )
*
* const childEffect = Effect.withSpan("child-operation")(
* Effect.succeed("child result")
* )
*
* // Link the child span to the parent span
* const program = Effect.gen(function*() {
* const parentSpan = yield* Effect.currentSpan
* const result = yield* childEffect.pipe(
* Effect.linkSpans(parentSpan, { relationship: "follows" })
* )
* return result
* })
* ```
*
* **Example** (Linking multiple spans at once)
*
* ```ts
* import { Effect } from "effect"
*
* // Link multiple spans
* const program = Effect.gen(function*() {
* const span1 = yield* Effect.currentSpan
* const span2 = yield* Effect.currentSpan
*
* return yield* Effect.succeed("result").pipe(
* Effect.linkSpans([span1, span2], {
* type: "dependency",
* source: "multiple-operations"
* })
* )
* })
* ```
*
* @category tracing
* @since 2.0.0
*/
export const linkSpans = internal.linkSpans;
/**
* Creates a new tracing span and returns it without managing its lifetime.
*
* **Details**
*
* The span is not added to the current span stack and is not ended
* automatically. Use `withSpan`, `useSpan`, or `makeSpanScoped` when the span
* should be installed as context or closed automatically.
*
* **Example** (Creating a span manually)
*
* ```ts
* import { Effect } from "effect"
*
* const program = Effect.gen(function*() {
* const span = yield* Effect.makeSpan("my-operation")
* yield* Effect.log("Operation in progress")
* return "completed"
* })
* ```
*
* @category tracing
* @since 2.0.0
*/
export const makeSpan = internal.makeSpan;
/**
* Create a new span for tracing, and automatically close it when the Scope
* finalizes.
*
* **Details**
*
* The span is not added to the current span stack, so no child spans will be
* created for it.
*
* **Example** (Creating a scoped standalone span)
*
* ```ts
* import { Effect } from "effect"
*
* const program = Effect.scoped(
* Effect.gen(function*() {
* const span = yield* Effect.makeSpanScoped("scoped-operation")
* yield* Effect.log("Working...")
* return "done"
* // Span automatically closes when scope ends
* })
* )
* ```
*
* @category tracing
* @since 2.0.0
*/
export const makeSpanScoped = internal.makeSpanScoped;
/**
* Create a new span for tracing, and automatically close it when the effect
* completes.
*
* **Details**
*
* The span is not added to the current span stack, so no child spans will be
* created for it.
*
* **Example** (Running an effect with a standalone span)
*
* ```ts
* import { Effect } from "effect"
*
* const program = Effect.useSpan(
* "user-operation",
* (span) =>
* Effect.gen(function*() {
* yield* Effect.log("Processing user data")
* return "success"
* })
* )
* ```
*
* @category tracing
* @since 2.0.0
*/
export const useSpan = internal.useSpan;
/**
* Wraps the effect with a child span for tracing.
*
* **Example** (Wrapping an effect in a child span)
*
* ```ts
* import { Effect } from "effect"
*
* const task = Effect.gen(function*() {
* yield* Effect.log("Executing task")
* return "result"
* })
*
* const traced = Effect.withSpan(task, "my-task", {
* attributes: { version: "1.0" }
* })
* ```
*
* @category tracing
* @since 2.0.0
*/
export const withSpan = internal.withSpan;
/**
* Wraps the effect with a scoped child span for tracing.
*
* **Details**
*
* The span is ended when the Scope is finalized.
*
* **Example** (Creating a scoped child span)
*
* ```ts
* import { Effect } from "effect"
*
* const program = Effect.scoped(
* Effect.gen(function*() {
* const task = Effect.log("Working...")
* yield* Effect.withSpanScoped(task, "scoped-task")
* return "completed"
* })
* )
* ```
*
* @category tracing
* @since 2.0.0
*/
export const withSpanScoped = internal.withSpanScoped;
/**
* Adds the provided span to the current span stack.
*
* **Example** (Setting a parent span)
*
* ```ts
* import { Effect } from "effect"
*
* const program = Effect.gen(function*() {
* const span = yield* Effect.makeSpan("parent-span")
* const childTask = Effect.log("Child operation")
* yield* Effect.withParentSpan(childTask, span)
* return "completed"
* })
* ```
*
* @category tracing
* @since 2.0.0
*/
export const withParentSpan = internal.withParentSpan;
// -----------------------------------------------------------------------------
// Batching
// -----------------------------------------------------------------------------
/**
* Executes a request using the provided resolver.
*
* **When to use**
*
* Use when you need resolver-driven batching for a typed `Request`.
*
* **Example** (Executing a request through a resolver)
*
* ```ts
* import { Console, Effect, Exit, Request, RequestResolver } from "effect"
*
* interface GetUser extends Request.Request<string> {
* readonly _tag: "GetUser"
* readonly id: number
* }
* const GetUser = Request.tagged<GetUser>("GetUser")
*
* const resolver = RequestResolver.make<GetUser>(
* Effect.fnUntraced(function*(entries) {
* for (const entry of entries) {
* yield* Request.complete(entry, Exit.succeed(`user-${entry.request.id}`))
* }
* })
* )
*
* const program = Effect.gen(function*() {
* const name = yield* Effect.request(GetUser({ id: 1 }), resolver)
* yield* Console.log(name)
* })
* ```
*
* @see {@link requestUnsafe} for the low-level entry point when you already have a `Context` and need to enqueue outside an `Effect`
*
* @category requests & batching
* @since 2.0.0
*/
export const request = internalRequest.request;
/**
* Registers a request with a resolver and delivers the exit value via `onExit`.
*
* **When to use**
*
* Use when you already have a `Context` and need to enqueue a request outside
* an `Effect` while receiving completion through `onExit`.
*
* **Details**
*
* It returns a canceler that removes the pending request entry.
*
* @see {@link request} for the `Effect`-returning API used for normal request execution
*
* @category requests & batching
* @since 4.0.0
*/
export const requestUnsafe = internalRequest.requestUnsafe;
// -----------------------------------------------------------------------------
// Supervision & Fiber's
// -----------------------------------------------------------------------------
/**
* Returns an effect that forks this effect into its own separate fiber,
* returning the fiber immediately, without waiting for it to begin executing
* the effect.
*
* **Details**
*
* You can use the `forkChild` method whenever you want to execute an effect in a
* new fiber, concurrently and without "blocking" the fiber executing other
* effects. Using fibers can be tricky, so instead of using this method
* directly, consider other higher-level methods, such as `raceWith`,
* `zipPar`, and so forth.
*
* The fiber returned by this method has methods to interrupt the fiber and to
* wait for it to finish executing the effect. See `Fiber` for more
* information.
*
* Whenever you use this method to launch a new fiber, the new fiber is
* attached to the parent fiber's scope. This means when the parent fiber
* terminates, the child fiber will be terminated as well, ensuring that no
* fibers leak. This behavior is called "auto supervision", and if this
* behavior is not desired, you may use the `forkDetach` or `forkIn` methods.
*
* **Example** (Forking a child fiber)
*
* ```ts
* import { Effect, Fiber } from "effect"
*
* const longRunningTask = Effect.gen(function*() {
* yield* Effect.sleep("2 seconds")
* yield* Effect.log("Task completed")
* return "result"
* })
*
* const program = Effect.gen(function*() {
* const fiber = yield* longRunningTask.pipe(Effect.forkChild)
*
* // or fork a fiber that starts immediately:
* yield* longRunningTask.pipe(Effect.forkChild({ startImmediately: true }))
*
* yield* Effect.log("Task forked, continuing...")
* const result = yield* Fiber.join(fiber)
* return result
* })
* ```
*
* @category supervision & fibers
* @since 4.0.0
*/
export const forkChild = internal.forkChild;
/**
* Forks the effect in the specified scope. The fiber will be interrupted
* when the scope is closed.
*
* **Example** (Forking into a supplied scope)
*
* ```ts
* import { Effect } from "effect"
*
* const task = Effect.gen(function*() {
* yield* Effect.sleep("10 seconds")
* return "completed"
* })
*
* const program = Effect.scoped(
* Effect.gen(function*() {
* const scope = yield* Effect.scope
* const fiber = yield* Effect.forkIn(task, scope)
* yield* Effect.sleep("1 second")
* // Fiber will be interrupted when scope closes
* return "done"
* })
* )
* ```
*
* @category supervision & fibers
* @since 2.0.0
*/
export const forkIn = internal.forkIn;
/**
* Forks the fiber in a `Scope`, interrupting it when the scope is closed.
*
* **Example** (Forking into the current scope)
*
* ```ts
* import { Effect } from "effect"
*
* const backgroundTask = Effect.gen(function*() {
* yield* Effect.sleep("5 seconds")
* yield* Effect.log("Background task completed")
* return "result"
* })
*
* const program = Effect.scoped(
* Effect.gen(function*() {
* const fiber = yield* backgroundTask.pipe(Effect.forkScoped)
*
* // or fork a fiber that starts immediately:
* yield* backgroundTask.pipe(Effect.forkScoped({ startImmediately: true }))
*
* yield* Effect.log("Task forked in scope")
* yield* Effect.sleep("1 second")
*
* // Fiber will be interrupted when scope closes
* return "scope completed"
* })
* )
* ```
*
* @category supervision & fibers
* @since 2.0.0
*/
export const forkScoped = internal.forkScoped;
/**
* Forks the effect into a new fiber attached to the global scope. Because the
* new fiber is attached to the global scope, when the fiber executing the
* returned effect terminates, the forked fiber will continue running.
*
* **Example** (Forking a detached fiber)
*
* ```ts
* import { Effect } from "effect"
*
* const daemonTask = Effect.gen(function*() {
* while (true) {
* yield* Effect.sleep("1 second")
* yield* Effect.log("Daemon running...")
* }
* })
*
* const program = Effect.gen(function*() {
* const fiber = yield* daemonTask.pipe(Effect.forkDetach)
*
* // or fork a fiber that starts immediately:
* yield* daemonTask.pipe(Effect.forkDetach({ startImmediately: true }))
*
* yield* Effect.log("Daemon started")
* yield* Effect.sleep("3 seconds")
* // Daemon continues running after this effect completes
* return "main completed"
* })
* ```
*
* @category supervision & fibers
* @since 4.0.0
*/
export const forkDetach = internal.forkDetach;
/**
* Waits for all child fibers forked by this effect to complete before this
* effect completes.
*
* **When to use**
*
* Use to let an effect start child work concurrently while still delaying its
* own completion until that child work is done.
*
* **Gotchas**
*
* Child fibers that already exist before the wrapped effect starts are not
* awaited.
*
* @see {@link forkChild} for forking child fibers that are awaited by this operator
* @see {@link forkDetach} for forking fibers outside the child scope
* @see {@link forkIn} for forking into an explicit scope
* @see {@link forkScoped} for forking fibers tied to the current scope
*
* @category supervision & fibers
* @since 2.0.0
*/
export const awaitAllChildren = internal.awaitAllChildren;
/**
* Accesses the fiber currently executing the effect.
*
* **Example** (Accessing the current fiber)
*
* ```ts
* import { Console, Effect } from "effect"
*
* const program = Effect.gen(function*() {
* const fiber = yield* Effect.fiber
* yield* Console.log(`Fiber id: ${fiber.id}`)
* })
* ```
*
* @category supervision & fibers
* @since 4.0.0
*/
export const fiber = internal.fiber;
/**
* Accesses the current fiber id executing the effect.
*
* **Example** (Accessing the current fiber id)
*
* ```ts
* import { Effect } from "effect"
*
* const program = Effect.log("event").pipe(
* // Read the current span with the fiber id for tagging.
* Effect.andThen(Effect.all([Effect.currentSpan, Effect.fiberId])),
* Effect.withSpan("A"),
* Effect.map(([span, fiberId]) => ({
* spanName: span.name,
* fiberId
* }))
* )
* ```
*
* @category supervision & fibers
* @since 2.0.0
*/
export const fiberId = internal.fiberId;
/**
* Runs an effect in the background, returning a fiber that can
* be observed or interrupted.
*
* **When to use**
*
* Use when you need to start an effect in the background and receive a fiber.
*
* **Example** (Running an effect in the background)
*
* ```ts
* import { Console, Effect, Fiber, Schedule } from "effect"
*
* // ┌─── Effect<number, never, never>
* // ▼
* const program = Effect.repeat(
* Console.log("running..."),
* Schedule.spaced("200 millis")
* )
*
* // ┌─── RuntimeFiber<number, never>
* // ▼
* const fiber = Effect.runFork(program)
*
* setTimeout(() => {
* Effect.runFork(Fiber.interrupt(fiber))
* }, 500)
* ```
*
* @category running
* @since 2.0.0
*/
export const runFork = internal.runFork;
/**
* Runs an effect in the background with the provided services.
*
* **When to use**
*
* Use when an effect still requires services, you already have a `Context`, and
* you want a background fiber.
*
* **Example** (Running with services in the background)
*
* ```ts
* import { Context, Effect } from "effect"
*
* interface Logger {
* log: (message: string) => void
* }
*
* const Logger = Context.Service<Logger>("Logger")
*
* const services = Context.make(Logger, {
* log: (message) => console.log(message)
* })
*
* const program = Effect.gen(function*() {
* const logger = yield* Logger
* logger.log("Hello from service!")
* return "done"
* })
*
* const fiber = Effect.runForkWith(services)(program)
* ```
*
* @category running
* @since 4.0.0
*/
export const runForkWith = internal.runForkWith;
/**
* Forks an effect with the provided services, registers `onExit` as a fiber observer, and returns an interruptor.
*
* **When to use**
*
* Use when embedding an effect into callback-style code with explicit services
* and a synchronous interruptor.
*
* **Details**
*
* The returned interruptor calls `fiber.interruptUnsafe`, optionally with an interruptor id.
*
* **Example** (Running with services and a callback)
*
* ```ts
* import { Console, Context, Effect, Exit } from "effect"
*
* interface Logger {
* log: (message: string) => Effect.Effect<void>
* }
*
* const Logger = Context.Service<Logger>("Logger")
*
* const services = Context.make(Logger, {
* log: (message) => Console.log(message)
* })
*
* const program = Effect.gen(function*() {
* const logger = yield* Logger
* yield* logger.log("Started")
* return "done"
* })
*
* const interrupt = Effect.runCallbackWith(services)(program, {
* onExit: (exit) => {
* if (Exit.isFailure(exit)) {
* // handle failure or interruption
* }
* }
* })
*
* // Use the interruptor if you need to cancel the fiber later.
* interrupt()
* ```
*
* @category running
* @since 4.0.0
*/
export const runCallbackWith = internal.runCallbackWith;
/**
* Runs an effect asynchronously, registering `onExit` as a fiber observer and
* returning an interruptor.
*
* **Details**
*
* The interruptor calls `fiber.interruptUnsafe` with the optional interruptor
* id.
*
* **Example** (Running with a callback)
*
* ```ts
* import { Console, Effect, Exit } from "effect"
*
* const program = Effect.gen(function*() {
* yield* Console.log("working")
* return "done"
* })
*
* const interrupt = Effect.runCallback(program, {
* onExit: (exit) => {
* Effect.runSync(
* Exit.match(exit, {
* onFailure: () => Console.log("failed"),
* onSuccess: (value) => Console.log(`success: ${value}`)
* })
* )
* }
* })
*
* // Output:
* // working
* // success: done
*
* // interrupt() to cancel the fiber if needed
* ```
*
* @category running
* @since 2.0.0
*/
export const runCallback = internal.runCallback;
/**
* Executes an effect and returns the result as a `Promise`.
*
* **When to use**
*
* Use when you need to execute an effect and work with the
* result using `Promise` syntax, typically for compatibility with other
* promise-based code.
*
* If the effect succeeds, the promise will resolve with the result. If the
* effect fails, the promise will reject with an error.
*
* **Example** (Running a successful effect as a Promise)
*
* ```ts
* import { Effect } from "effect"
*
* Effect.runPromise(Effect.succeed(1)).then(console.log)
* // Output: 1
* ```
*
* **Example** (Running effects as promises)
*
* ```ts
* //Example: Handling a Failing Effect as a Rejected Promise
* import { Effect } from "effect"
*
* Effect.runPromise(Effect.fail("my error")).catch(console.error)
* // Output:
* // (FiberFailure) Error: my error
* ```
*
* @see {@link runPromiseExit} for a version that returns an `Exit` type instead of rejecting.
* @category running
* @since 2.0.0
*/
export const runPromise = internal.runPromise;
/**
* Executes an effect as a Promise with the provided services.
*
* **When to use**
*
* Use when you already have a `Context` and need Promise interop that rejects on
* effect failure.
*
* **Example** (Running with services as a promise)
*
* ```ts
* import { Context, Effect } from "effect"
*
* interface Config {
* apiUrl: string
* }
*
* const Config = Context.Service<Config>("Config")
*
* const context = Context.make(Config, {
* apiUrl: "https://api.example.com"
* })
*
* const program = Effect.gen(function*() {
* const config = yield* Config
* return `Connecting to ${config.apiUrl}`
* })
*
* Effect.runPromiseWith(context)(program).then(console.log)
* ```
*
* @category running
* @since 4.0.0
*/
export const runPromiseWith = internal.runPromiseWith;
/**
* Runs an effect and returns a `Promise` that resolves to an `Exit`, which
* represents the outcome (success or failure) of the effect.
*
* **When to use**
*
* Use when you need to determine if an effect succeeded
* or failed, including any defects, and you want to work with a `Promise`.
*
* **Details**
*
* The `Exit` type represents the result of the effect. Successful effects are
* wrapped in `Success`, and failed effects are wrapped in `Failure` with a
* `Cause`.
*
* **Example** (Observing promise results as Exit)
*
* ```ts
* import { Effect } from "effect"
*
* // Execute a successful effect and get the Exit result as a Promise
* Effect.runPromiseExit(Effect.succeed(1)).then(console.log)
* // Output:
* // {
* // _id: "Exit",
* // _tag: "Success",
* // value: 1
* // }
*
* // Execute a failing effect and get the Exit result as a Promise
* Effect.runPromiseExit(Effect.fail("my error")).then(console.log)
* // Output:
* // {
* // _id: "Exit",
* // _tag: "Failure",
* // cause: {
* // _id: "Cause",
* // _tag: "Fail",
* // failure: "my error"
* // }
* // }
* ```
*
* @see {@link runPromise} for a version that rejects on failure.
*
* @category running
* @since 2.0.0
*/
export const runPromiseExit = internal.runPromiseExit;
/**
* Runs an effect and returns a Promise of Exit with provided services.
*
* **When to use**
*
* Use when you already have a `Context` and need Promise interop that preserves
* success and failure as an `Exit`.
*
* **Example** (Running with services as an Exit promise)
*
* ```ts
* import { Context, Effect, Exit } from "effect"
*
* interface Database {
* query: (sql: string) => string
* }
*
* const Database = Context.Service<Database>("Database")
*
* const services = Context.make(Database, {
* query: (sql) => `Result for: ${sql}`
* })
*
* const program = Effect.gen(function*() {
* const db = yield* Database
* return db.query("SELECT * FROM users")
* })
*
* Effect.runPromiseExitWith(services)(program).then((exit) => {
* if (Exit.isSuccess(exit)) {
* console.log("Success:", exit.value)
* }
* })
* ```
*
* @category running
* @since 4.0.0
*/
export const runPromiseExitWith = internal.runPromiseExitWith;
/**
* Executes an effect synchronously and returns its success value.
*
* **When to use**
*
* Use when you need to execute an effect that is guaranteed to complete
* synchronously.
*
* **Details**
*
* If the effect fails, dies, is interrupted, or performs asynchronous work,
* `runSync` throws a `FiberFailure` instead of returning a value. Use
* `runSyncExit` when you want the failure captured as an `Exit`.
*
* **Example** (Running a synchronous effect)
*
* ```ts
* import { Effect } from "effect"
*
* const program = Effect.sync(() => {
* console.log("Hello, World!")
* return 1
* })
*
* const result = Effect.runSync(program)
* // Output: Hello, World!
*
* console.log(result)
* // Output: 1
* ```
*
* **Example** (Throwing for failed or async effects)
*
* ```ts
* import { Effect } from "effect"
*
* try {
* // Attempt to run an effect that fails
* Effect.runSync(Effect.fail("my error"))
* } catch (e) {
* console.error(e)
* }
* // Output:
* // (FiberFailure) Error: my error
*
* try {
* // Attempt to run an effect that involves async work
* Effect.runSync(Effect.promise(() => Promise.resolve(1)))
* } catch (e) {
* console.error(e)
* }
* // Output:
* // (FiberFailure) AsyncFiberException: Fiber #0 cannot be resolved synchronously. This is caused by using runSync on an effect that performs async work
* ```
*
* @see {@link runSyncExit} for a version that returns an `Exit` type instead of
* throwing an error.
* @category running
* @since 2.0.0
*/
export const runSync = internal.runSync;
/**
* Executes an effect synchronously with provided services.
*
* **When to use**
*
* Use when you already have a `Context`, the effect is known to complete
* synchronously, and failures should throw.
*
* **Example** (Running synchronously with services)
*
* ```ts
* import { Context, Effect } from "effect"
*
* interface MathService {
* add: (a: number, b: number) => number
* }
*
* const MathService = Context.Service<MathService>("MathService")
*
* const context = Context.make(MathService, {
* add: (a, b) => a + b
* })
*
* const program = Effect.gen(function*() {
* const math = yield* MathService
* return math.add(2, 3)
* })
*
* const result = Effect.runSyncWith(context)(program)
* console.log(result) // 5
* ```
*
* @category running
* @since 4.0.0
*/
export const runSyncWith = internal.runSyncWith;
/**
* Runs an effect synchronously and captures the outcome safely as an `Exit` type, which
* represents the outcome (success or failure) of the effect.
*
* **When to use**
*
* Use to find out whether an effect succeeded or failed,
* including any defects, without dealing with asynchronous operations.
*
* **Details**
*
* The `Exit` type represents the result of the effect. Successful effects are
* wrapped in `Success`, and failed effects are wrapped in `Failure` with a
* `Cause`.
*
* If the effect contains asynchronous operations, `runSyncExit` will
* return an `Failure` with a `Die` cause, indicating that the effect cannot be
* resolved synchronously.
*
* **Example** (Observing synchronous results as Exit)
*
* ```ts
* import { Effect } from "effect"
*
* console.log(Effect.runSyncExit(Effect.succeed(1)))
* // Output:
* // {
* // _id: "Exit",
* // _tag: "Success",
* // value: 1
* // }
*
* console.log(Effect.runSyncExit(Effect.fail("my error")))
* // Output:
* // {
* // _id: "Exit",
* // _tag: "Failure",
* // cause: {
* // _id: "Cause",
* // _tag: "Fail",
* // failure: "my error"
* // }
* // }
* ```
*
* **Example** (Capturing async work as a Die cause)
*
* ```ts
* import { Effect } from "effect"
*
* console.log(Effect.runSyncExit(Effect.promise(() => Promise.resolve(1))))
* // Output:
* // {
* // _id: 'Exit',
* // _tag: 'Failure',
* // cause: {
* // _id: 'Cause',
* // _tag: 'Die',
* // defect: [Fiber #0 cannot be resolved synchronously. This is caused by using runSync on an effect that performs async work] {
* // fiber: [FiberRuntime],
* // _tag: 'AsyncFiberException',
* // name: 'AsyncFiberException'
* // }
* // }
* // }
* ```
*
* @see {@link runSync} for a version that throws on failure.
*
* @category running
* @since 2.0.0
*/
export const runSyncExit = internal.runSyncExit;
/**
* Runs an effect synchronously with provided services, returning an Exit result safely.
*
* **When to use**
*
* Use when you already have a `Context` and need a synchronous `Exit` instead of
* throwing on failure.
*
* **Example** (Running synchronously with services as Exit)
*
* ```ts
* import { Context, Effect, Exit } from "effect"
*
* // Define a logger service
* const Logger = Context.Service<{
* log: (msg: string) => void
* }>("Logger")
*
* const program = Effect.gen(function*() {
* const logger = yield* Effect.service(Logger)
* logger.log("Computing result...")
* return 42
* })
*
* // Prepare context
* const context = Context.make(Logger, {
* log: (msg) => console.log(`[LOG] ${msg}`)
* })
*
* const exit = Effect.runSyncExitWith(context)(program)
*
* if (Exit.isSuccess(exit)) {
* console.log(`Success: ${exit.value}`)
* } else {
* console.log(`Failure: ${exit.cause}`)
* }
* // Output:
* // [LOG] Computing result...
* // Success: 42
* ```
*
* @category running
* @since 4.0.0
*/
export const runSyncExitWith = internal.runSyncExitWith;
/**
* Creates an Effect-returning function without tracing.
*
* **When to use**
*
* Use when you are defining a reusable Effect function whose implementation
* would otherwise be a normal function returning {@link gen}, especially when
* tracing spans or stack-frame capture are not needed.
*
* **Details**
*
* Compared to a plain function that returns {@link gen}, `Effect.fnUntraced`
* reuses the generator body instead of allocating a fresh generator closure
* around the arguments on every call. It does not record an Effect stack-frame
* boundary and does not create tracing spans. Use {@link fn} when you need
* those stack frames or spans. Additional arguments after the generator body
* act like `pipe` transforms: each transform receives the previous result and
* the original function arguments. Annotate the generator return type with
* `Effect.fn.Return<A, E, R>` when the produced `Effect` type needs to be
* stated explicitly.
*
* **Example** (Defining untraced effect functions)
*
* ```ts
* import { Effect } from "effect"
*
* const f = Effect.fnUntraced(function*(
* value: string
* ) {
* return yield* Effect.succeed(value.length)
* })
*
* // ┌─── Effect.Effect<number>
* // ▼
* const program = f("hello")
* ```
*
* **Example** (Transforming the returned Effect)
*
* ```ts
* import { Effect } from "effect"
*
* const f = Effect.fnUntraced(
* function*(value: string) {
* return yield* Effect.succeed(value.length)
* },
* (effect, value) =>
* effect.pipe(Effect.map((length) => `${value}: ${length}`))
* )
*
* // ┌─── Effect.Effect<string>
* // ▼
* const program = f("hello")
* ```
*
* **Example** (Annotating an untraced non-parametric function)
*
* ```ts
* import { Effect } from "effect"
*
* const f = Effect.fnUntraced(function*(
* value: string
* ): Effect.fn.Return<number> {
* return yield* Effect.succeed(value.length)
* })
*
* // ┌─── Effect.Effect<number>
* // ▼
* const program = f("hello")
* ```
*
* **Example** (Annotating an untraced parametric function)
*
* ```ts
* import { Effect } from "effect"
*
* const f = Effect.fnUntraced(function*<A>(
* value: A
* ): Effect.fn.Return<A> {
* return yield* Effect.succeed(value)
* })
*
* // ┌─── Effect.Effect<string>
* // ▼
* const program = f("hello")
* ```
*
* @category functions
* @since 3.12.0
*/
export const fnUntraced = internal.fnUntraced;
/**
* Creates a reusable traced function from an Effect body.
*
* **When to use**
*
* Use when you are defining a reusable Effect function whose implementation
* would otherwise be a normal function returning {@link gen}, and you want
* tracing spans or stack-frame capture.
*
* **Details**
*
* Compared to a plain function that returns {@link gen}, `Effect.fn` reuses the
* generator body instead of allocating a fresh generator closure around the
* arguments on every call. Call `Effect.fn(body, ...)` for a generic
* stack-frame boundary without creating a span. Call
* `Effect.fn("operationName", options?)(body, ...)` when that boundary should
* have a readable operation name and the returned `Effect` should create a
* tracing span when run. {@link SpanOptionsNoTrace} configures span metadata
* such as attributes, links, parent or root selection, kind, sampling, and log
* level. Additional arguments after the generator body act like `pipe`
* transforms: each transform receives the previous result and the original
* function arguments. When those transforms return an `Effect`, the returned
* effect includes stack-frame metadata and, for the named form, a tracing span.
* Generator bodies may declare a `this` parameter; pass `{ self }` before the
* body to bind `this` when the function is created.
*
* **Example** (Defining traced effect functions)
*
* ```ts
* import { Effect } from "effect"
*
* const f = Effect.fn("calculateLength")(function*(value: string) {
* return yield* Effect.succeed(value.length)
* })
*
* // ┌─── Effect.Effect<number>
* // ▼
* const program = f("hello")
* ```
*
* **Example** (Transforming the returned Effect)
*
* ```ts
* import { Effect } from "effect"
*
* const f = Effect.fn("formatLength")(
* function*(value: string) {
* return yield* Effect.succeed(value.length)
* },
* (effect, value) =>
* effect.pipe(Effect.map((length) => `${value}: ${length}`))
* )
*
* // ┌─── Effect.Effect<string>
* // ▼
* const program = f("hello")
* ```
*
* **Example** (Binding this)
*
* ```ts
* import { Effect } from "effect"
*
* class Counter {
* count = 0
*
* increment = Effect.fn("Counter.increment")(
* { self: this },
* function*(this: Counter, by: number) {
* this.count += by
* return yield* Effect.succeed(this.count)
* }
* )
* }
*
* const counter = new Counter()
*
* // ┌─── Effect.Effect<number>
* // ▼
* const program = counter.increment(1)
* ```
*
* **Example** (Annotating a traced non-parametric function)
*
* ```ts
* import { Effect } from "effect"
*
* const f = Effect.fn("calculateLength")(function*(
* value: string
* ): Effect.fn.Return<number> {
* return yield* Effect.succeed(value.length)
* })
*
* // ┌─── Effect.Effect<number>
* // ▼
* const program = f("hello")
* ```
*
* **Example** (Annotating a traced parametric function)
*
* ```ts
* import { Effect } from "effect"
*
* const f = Effect.fn("succeed")(function*<A>(
* value: A
* ): Effect.fn.Return<A> {
* return yield* Effect.succeed(value)
* })
*
* // ┌─── Effect.Effect<string>
* // ▼
* const program = f("hello")
* ```
*
* @category functions
* @since 3.11.0
*/
export const fn = internal.fn;
// ========================================================================
// Clock
// ========================================================================
/**
* Retrieves the `Clock` service from the context and provides it to the
* specified effectful function.
*
* **Example** (Accessing the Clock service)
*
* ```ts
* import { Console, Effect } from "effect"
*
* const program = Effect.clockWith((clock) =>
* clock.currentTimeMillis.pipe(
* Effect.map((currentTime) => `Current time is: ${currentTime}`),
* Effect.tap(Console.log)
* )
* )
*
* Effect.runFork(program)
* // Example Output:
* // Current time is: 1735484929744
* ```
*
* @category clock
* @since 2.0.0
*/
export const clockWith = internal.clockWith;
// ========================================================================
// Logging
// ========================================================================
/**
* Creates a logger function that logs at the specified level.
*
* **Details**
*
* If no level is provided, the logger uses the fiber's current log level and
* extracts any `Cause` values from the message list.
*
* **Example** (Logging at a dynamic level)
*
* ```ts
* import { Effect } from "effect"
*
* const logWarn = Effect.logWithLevel("Warn")
*
* const program = Effect.gen(function*() {
* yield* logWarn("Cache miss", { key: "user:1" })
* })
* ```
*
* @category logging
* @since 2.0.0
*/
export const logWithLevel = internal.logWithLevel;
/**
* Logs one or more messages using the default log level.
*
* **Example** (Logging at the default level)
*
* ```ts
* import { Effect } from "effect"
*
* const program = Effect.gen(function*() {
* yield* Effect.log("Starting computation")
* const result = 2 + 2
* yield* Effect.log("Result:", result)
* yield* Effect.log("Multiple", "values", "can", "be", "logged")
* return result
* })
*
* Effect.runPromise(program).then(console.log)
* // Output:
* // timestamp=2023-... level=INFO message="Starting computation"
* // timestamp=2023-... level=INFO message="Result: 4"
* // timestamp=2023-... level=INFO message="Multiple values can be logged"
* // 4
* ```
*
* @category logging
* @since 2.0.0
*/
export const log = /*#__PURE__*/internal.logWithLevel();
/**
* Logs one or more messages at the FATAL level.
*
* **Example** (Logging fatal messages)
*
* ```ts
* import { Effect } from "effect"
*
* const program = Effect.gen(function*() {
* try {
* // Simulate a critical system failure
* throw new Error("System memory exhausted")
* } catch (error) {
* const errorMessage = error instanceof Error ? error.message : String(error)
* yield* Effect.logFatal("Critical system failure:", errorMessage)
* yield* Effect.logFatal("System shutting down")
* }
* })
*
* Effect.runPromise(program)
* // Output:
* // timestamp=2023-... level=FATAL message="Critical system failure: System memory exhausted"
* // timestamp=2023-... level=FATAL message="System shutting down"
* ```
*
* @category logging
* @since 2.0.0
*/
export const logFatal = /*#__PURE__*/internal.logWithLevel("Fatal");
/**
* Logs one or more messages at the WARNING level.
*
* **Example** (Logging warnings)
*
* ```ts
* import { Effect } from "effect"
*
* const program = Effect.gen(function*() {
* yield* Effect.logWarning("API rate limit approaching")
* yield* Effect.logWarning("Retries remaining:", 2, "Operation:", "fetchData")
*
* // Useful for non-critical issues
* const deprecated = true
* if (deprecated) {
* yield* Effect.logWarning("Using deprecated API endpoint")
* }
* })
*
* Effect.runPromise(program)
* // Output:
* // timestamp=2023-... level=WARN message="API rate limit approaching"
* // timestamp=2023-... level=WARN message="Retries remaining: 2 Operation: fetchData"
* // timestamp=2023-... level=WARN message="Using deprecated API endpoint"
* ```
*
* @category logging
* @since 2.0.0
*/
export const logWarning = /*#__PURE__*/internal.logWithLevel("Warn");
/**
* Logs one or more messages at the ERROR level.
*
* **Example** (Logging errors)
*
* ```ts
* import { Effect } from "effect"
*
* const program = Effect.gen(function*() {
* yield* Effect.logError("Database connection failed")
* yield* Effect.logError(
* "Error code:",
* 500,
* "Message:",
* "Internal server error"
* )
*
* // Can be used with error objects
* const error = new Error("Something went wrong")
* yield* Effect.logError("Caught error:", error.message)
* })
*
* Effect.runPromise(program)
* // Output:
* // timestamp=2023-... level=ERROR message="Database connection failed"
* // timestamp=2023-... level=ERROR message="Error code: 500 Message: Internal server error"
* // timestamp=2023-... level=ERROR message="Caught error: Something went wrong"
* ```
*
* @category logging
* @since 2.0.0
*/
export const logError = /*#__PURE__*/internal.logWithLevel("Error");
/**
* Logs one or more messages at the INFO level.
*
* **Example** (Logging information)
*
* ```ts
* import { Effect } from "effect"
*
* const program = Effect.gen(function*() {
* yield* Effect.logInfo("Application starting up")
* yield* Effect.logInfo("Config loaded:", "production", "Port:", 3000)
*
* // Useful for general information
* const version = "1.2.3"
* yield* Effect.logInfo("Application version:", version)
* })
*
* Effect.runPromise(program)
* // Output:
* // timestamp=2023-... level=INFO message="Application starting up"
* // timestamp=2023-... level=INFO message="Config loaded: production Port: 3000"
* // timestamp=2023-... level=INFO message="Application version: 1.2.3"
* ```
*
* @category logging
* @since 2.0.0
*/
export const logInfo = /*#__PURE__*/internal.logWithLevel("Info");
/**
* Logs one or more messages at the DEBUG level.
*
* **Example** (Logging debug messages)
*
* ```ts
* import { Effect } from "effect"
*
* const program = Effect.gen(function*() {
* yield* Effect.logDebug("Debug mode enabled")
*
* const userInput = { name: "Alice", age: 30 }
* yield* Effect.logDebug("Processing user input:", userInput)
*
* // Useful for detailed diagnostic information
* yield* Effect.logDebug("Variable state:", "x=10", "y=20", "z=30")
* })
*
* Effect.runPromise(program)
* // Output:
* // timestamp=2023-... level=DEBUG message="Debug mode enabled"
* // timestamp=2023-... level=DEBUG message="Processing user input: [object Object]"
* // timestamp=2023-... level=DEBUG message="Variable state: x=10 y=20 z=30"
* ```
*
* @category logging
* @since 2.0.0
*/
export const logDebug = /*#__PURE__*/internal.logWithLevel("Debug");
/**
* Logs one or more messages at the TRACE level.
*
* **Example** (Logging trace messages)
*
* ```ts
* import { Effect } from "effect"
*
* const program = Effect.gen(function*() {
* yield* Effect.logTrace("Entering function processData")
*
* // Trace detailed execution flow
* for (let i = 0; i < 3; i++) {
* yield* Effect.logTrace("Loop iteration:", i, "Processing item")
* }
*
* yield* Effect.logTrace("Exiting function processData")
* })
*
* Effect.runPromise(program)
* // Output:
* // timestamp=2023-... level=TRACE message="Entering function processData"
* // timestamp=2023-... level=TRACE message="Loop iteration: 0 Processing item"
* // timestamp=2023-... level=TRACE message="Loop iteration: 1 Processing item"
* // timestamp=2023-... level=TRACE message="Loop iteration: 2 Processing item"
* // timestamp=2023-... level=TRACE message="Exiting function processData"
* ```
*
* @category logging
* @since 2.0.0
*/
export const logTrace = /*#__PURE__*/internal.logWithLevel("Trace");
/**
* Adds a logger to the set of loggers which will output logs for this effect.
*
* **Example** (Adding a logger to an effect)
*
* ```ts
* import { Effect, Logger } from "effect"
*
* // Create a custom logger that logs to the console
* const customLogger = Logger.make(({ message }) =>
* Effect.sync(() => console.log(`[CUSTOM]: ${message}`))
* )
*
* const program = Effect.gen(function*() {
* yield* Effect.log("This will go to both default and custom logger")
* return "completed"
* })
*
* // Add the custom logger to the effect
* const programWithLogger = Effect.withLogger(program, customLogger)
*
* Effect.runPromise(programWithLogger)
* // Output includes both default and custom log outputs
* ```
*
* @category logging
* @since 4.0.0
*/
export const withLogger = /*#__PURE__*/dual(2, (effect, logger) => internal.updateService(effect, internal.CurrentLoggers, loggers => new Set([...loggers, logger])));
/**
* Adds an annotation to each log line in this effect.
*
* **Example** (Adding log annotations)
*
* ```ts
* import { Effect } from "effect"
*
* const program = Effect.gen(function*() {
* yield* Effect.log("Starting operation")
* yield* Effect.log("Processing data")
* yield* Effect.log("Operation completed")
* })
*
* // Add annotations to all log messages
* const annotatedProgram = Effect.annotateLogs(program, {
* userId: "user123",
* operation: "data-processing"
* })
*
* // Also supports single key-value annotations
* const singleAnnotated = Effect.annotateLogs(program, "requestId", "req-456")
*
* Effect.runPromise(annotatedProgram)
* // All log messages will include the userId and operation annotations
* ```
*
* @category logging
* @since 2.0.0
*/
export const annotateLogs = /*#__PURE__*/dual(args => isEffect(args[0]), (effect, ...args) => internal.updateService(effect, CurrentLogAnnotations, annotations => {
const newAnnotations = {
...annotations
};
if (args.length === 1) {
Object.assign(newAnnotations, args[0]);
} else {
newAnnotations[args[0]] = args[1];
}
return newAnnotations;
}));
/**
* Adds log annotations to the current scope.
*
* **When to use**
*
* Use to attach log annotations that last until the current scope closes.
*
* **Details**
*
* This differs from `annotateLogs`, which only annotates a specific effect.
* `annotateLogsScoped` updates annotations for the entire current `Scope` and
* restores the previous annotations when the scope closes.
*
* **Example** (Adding scoped log annotations)
*
* ```ts
* import { Effect } from "effect"
*
* const program = Effect.scoped(
* Effect.gen(function*() {
* yield* Effect.log("before")
* yield* Effect.annotateLogsScoped({ requestId: "req-123" })
* yield* Effect.log("inside scope")
* })
* )
*
* Effect.runPromise(program)
* ```
*
* @see {@link annotateLogs} for annotating one effect
*
* @category logging
* @since 3.1.0
*/
export const annotateLogsScoped = internal.annotateLogsScoped;
/**
* Adds a span to each log line in this effect.
*
* **Example** (Adding a log span)
*
* ```ts
* import { Effect } from "effect"
*
* const databaseOperation = Effect.gen(function*() {
* yield* Effect.log("Connecting to database")
* yield* Effect.log("Executing query")
* yield* Effect.log("Processing results")
* return "data"
* })
*
* const httpRequest = Effect.gen(function*() {
* yield* Effect.log("Making HTTP request")
* const data = yield* Effect.withLogSpan(databaseOperation, "db-operation")
* yield* Effect.log("Sending response")
* return data
* })
*
* const program = Effect.withLogSpan(httpRequest, "http-handler")
*
* Effect.runPromise(program)
* // All log messages will include span information showing the nested operation context
* ```
*
* @category logging
* @since 2.0.0
*/
export const withLogSpan = /*#__PURE__*/dual(2, (effect, label) => internal.flatMap(internal.currentTimeMillis, now => internal.updateService(effect, CurrentLogSpans, spans => {
const span = [label, now];
return [span, ...spans];
})));
// -----------------------------------------------------------------------------
// Metrics
// -----------------------------------------------------------------------------
/**
* Updates the `Metric` every time the `Effect` is executed.
*
* **Details**
*
* Also accepts an optional function which can be used to map the `Exit` value
* of the `Effect` into a valid `Input` for the `Metric`.
*
* **Example** (Incrementing a metric for each execution)
*
* ```ts
* import { Effect, Metric } from "effect"
*
* const counter = Metric.counter("effect_executions", {
* description: "Counts effect executions"
* }).pipe(Metric.withConstantInput(1))
*
* const program = Effect.succeed("Hello").pipe(
* Effect.track(counter)
* )
*
* // This will increment the counter by 1 when executed
* Effect.runPromise(program).then(() =>
* Effect.runPromise(Metric.value(counter)).then(console.log)
* // Output: { count: 1, incremental: false }
* )
* ```
*
* **Example** (Mapping exits before updating a metric)
*
* ```ts
* import { Effect, Exit, Metric } from "effect"
*
* // Track different exit types with custom mapping
* const exitTracker = Metric.frequency("exit_types", {
* description: "Tracks success/failure/defect counts"
* })
*
* const mapExitToString = (exit: Exit.Exit<string, Error>) => {
* if (Exit.isSuccess(exit)) return "success"
* if (Exit.isFailure(exit)) return "failure"
* return "defect"
* }
*
* const effect = Effect.succeed("result").pipe(
* Effect.track(exitTracker, mapExitToString)
* )
* ```
*
* @category tracking
* @since 4.0.0
*/
export const track = /*#__PURE__*/dual(args => isEffect(args[0]), (self, metric, f) => onExit(self, exit => {
const input = f === undefined ? exit : internalCall(() => f(exit));
return Metric.update(metric, input);
}));
/**
* Updates the provided `Metric` every time the wrapped `Effect` succeeds with
* a value.
*
* **Details**
*
* Also accepts an optional function which can be used to map the success value
* of the `Effect` into a valid `Input` for the `Metric`.
*
* **Example** (Counting successful results)
*
* ```ts
* import { Effect, Metric } from "effect"
*
* const successCounter = Metric.counter("successes").pipe(
* Metric.withConstantInput(1)
* )
*
* const program = Effect.succeed(42).pipe(
* Effect.trackSuccesses(successCounter)
* )
*
* Effect.runPromise(program).then(() =>
* Effect.runPromise(Metric.value(successCounter)).then(console.log)
* // Output: { count: 1, incremental: false }
* )
* ```
*
* **Example** (Mapping successes before tracking)
*
* ```ts
* import { Effect, Metric } from "effect"
*
* // Track successful request sizes
* const requestSizeGauge = Metric.gauge("request_size_bytes")
*
* const program = Effect.succeed("Hello World!").pipe(
* Effect.trackSuccesses(requestSizeGauge, (value: string) => value.length)
* )
*
* Effect.runPromise(program).then(() =>
* Effect.runPromise(Metric.value(requestSizeGauge)).then(console.log)
* // Output: { value: 12 }
* )
* ```
*
* @category tracking
* @since 4.0.0
*/
export const trackSuccesses = /*#__PURE__*/dual(args => isEffect(args[0]), (self, metric, f) => tap(self, value => {
const input = f === undefined ? value : f(value);
return Metric.update(metric, input);
}));
/**
* Updates the provided `Metric` every time the wrapped `Effect` fails with an
* **expected** error.
*
* **Details**
*
* Also accepts an optional function which can be used to map the error value
* of the `Effect` into a valid `Input` for the `Metric`.
*
* **Example** (Counting expected failures)
*
* ```ts
* import { Effect, Metric } from "effect"
*
* const errorCounter = Metric.counter("errors").pipe(
* Metric.withConstantInput(1)
* )
*
* const program = Effect.fail("Network timeout").pipe(
* Effect.trackErrors(errorCounter)
* )
*
* Effect.runPromiseExit(program).then(() =>
* Effect.runPromise(Metric.value(errorCounter)).then(console.log)
* // Output: { count: 1, incremental: false }
* )
* ```
*
* **Example** (Mapping errors before tracking)
*
* ```ts
* import { Data, Effect, Metric } from "effect"
*
* class ConnectionFailedError extends Data.TaggedError("ConnectionFailedError")<{}> {}
*
* // Track error types using frequency metric
* const errorTypeFrequency = Metric.frequency("error_types")
*
* const program = Effect.fail(new ConnectionFailedError()).pipe(
* Effect.trackErrors(errorTypeFrequency, (error: ConnectionFailedError) => error._tag)
* )
*
* Effect.runPromiseExit(program).then(() =>
* Effect.runPromise(Metric.value(errorTypeFrequency)).then(console.log)
* // Output: { occurrences: Map(1) { "ConnectionFailedError" => 1 } }
* )
* ```
*
* @category tracking
* @since 4.0.0
*/
export const trackErrors = /*#__PURE__*/dual(args => isEffect(args[0]), (self, metric, f) => tapError(self, error => {
const input = f === undefined ? error : internalCall(() => f(error));
return Metric.update(metric, input);
}));
/**
* Updates the provided `Metric` every time the wrapped `Effect` fails with an
* **unexpected** error (i.e. a defect).
*
* **Details**
*
* Also accepts an optional function which can be used to map the defect value
* of the `Effect` into a valid `Input` for the `Metric`.
*
* **Example** (Counting defects)
*
* ```ts
* import { Effect, Metric } from "effect"
*
* const defectCounter = Metric.counter("defects").pipe(
* Metric.withConstantInput(1)
* )
*
* const program = Effect.die("Critical system failure").pipe(
* Effect.trackDefects(defectCounter)
* )
*
* Effect.runPromiseExit(program).then(() =>
* Effect.runPromise(Metric.value(defectCounter)).then(console.log)
* // Output: { count: 1, incremental: false }
* )
* ```
*
* **Example** (Mapping defects before tracking)
*
* ```ts
* import { Effect, Metric } from "effect"
*
* // Track defect types using frequency metric
* const defectTypeFrequency = Metric.frequency("defect_types")
*
* const program = Effect.die(new Error("Null pointer exception")).pipe(
* Effect.trackDefects(defectTypeFrequency, (defect: unknown) => {
* if (defect instanceof Error) return defect.constructor.name
* return typeof defect
* })
* )
*
* Effect.runPromiseExit(program).then(() =>
* Effect.runPromise(Metric.value(defectTypeFrequency)).then(console.log)
* // Output: { occurrences: Map(1) { "Error" => 1 } }
* )
* ```
*
* @category tracking
* @since 4.0.0
*/
export const trackDefects = /*#__PURE__*/dual(args => isEffect(args[0]), (self, metric, f) => tapDefect(self, defect => {
const input = f === undefined ? defect : internalCall(() => f(defect));
return Metric.update(metric, input);
}));
/**
* Updates the provided `Metric` with the `Duration` of time (in nanoseconds)
* that the wrapped `Effect` took to complete.
*
* **Details**
*
* Also accepts an optional function which can be used to map the `Duration`
* that the wrapped `Effect` took to complete into a valid `Input` for the
* `Metric`.
*
* **Example** (Recording execution duration)
*
* ```ts
* import { Effect, Metric } from "effect"
*
* const executionTimer = Metric.timer("execution_time")
*
* const program = Effect.sleep("100 millis").pipe(
* Effect.trackDuration(executionTimer)
* )
*
* Effect.runPromise(program).then(() =>
* Effect.runPromise(Metric.value(executionTimer)).then(console.log)
* // Output: { count: 1, min: 100000000, max: 100000000, sum: 100000000 }
* )
* ```
*
* **Example** (Mapping duration before tracking)
*
* ```ts
* import { Duration, Effect, Metric } from "effect"
*
* // Track execution time in milliseconds using custom mapping
* const durationGauge = Metric.gauge("execution_millis")
*
* const program = Effect.sleep("200 millis").pipe(
* Effect.trackDuration(durationGauge, (duration) => Duration.toMillis(duration))
* )
*
* Effect.runPromise(program).then(() =>
* Effect.runPromise(Metric.value(durationGauge)).then(console.log)
* // Output: { value: 200 }
* )
* ```
*
* @category tracking
* @since 4.0.0
*/
export const trackDuration = /*#__PURE__*/dual(args => isEffect(args[0]), (self, metric, f) => clockWith(clock => {
const startTime = clock.currentTimeNanosUnsafe();
return onExit(self, () => {
const endTime = clock.currentTimeNanosUnsafe();
const duration = Duration.subtract(Duration.fromInputUnsafe(endTime), Duration.fromInputUnsafe(startTime));
const input = f === undefined ? duration : internalCall(() => f(duration));
return Metric.update(metric, input);
});
}));
// -----------------------------------------------------------------------------
// Transactions
// -----------------------------------------------------------------------------
/**
* Service that holds the current transaction state.
*
* **Details**
*
* It includes a journal that stores non-committed changes to `TxRef` values and
* a retry flag that records whether the transaction should be retried.
*
* **Example** (Building transactions)
*
* ```ts
* import { Effect } from "effect"
*
* // Transaction class for software transactional memory operations
* const txEffect = Effect.gen(function*() {
* const tx = yield* Effect.Transaction
* // Use transaction for coordinated state changes
* return "Transaction complete"
* })
* ```
*
* @category transactions
* @since 4.0.0
*/
export class Transaction extends /*#__PURE__*/Context.Service()("effect/Effect/Transaction") {}
/**
* Defines a transaction boundary. Transactions are "all or nothing" with respect to changes
* made to transactional values (i.e. TxRef) that occur within the transaction body.
*
* **Details**
*
* If called inside an active transaction, `tx` composes with the current transaction and reuses
* its journal and retry state instead of creating a nested boundary.
*
* Effect transactions are optimistic with retry. A transaction is retried when
* its body explicitly calls `Effect.txRetry` and any accessed transactional
* value changes, or when any accessed transactional value changes because a
* different transaction commits before the current one.
*
* The outermost `tx` call creates the transaction boundary and commits or rolls back the full
* composed transaction.
*
* **Example** (Running a transaction)
*
* ```ts
* import { Effect, TxRef } from "effect"
*
* const program = Effect.gen(function*() {
* const ref1 = yield* TxRef.make(0)
* const ref2 = yield* TxRef.make(0)
*
* // Nested tx calls compose into the same transaction
* yield* Effect.tx(Effect.gen(function*() {
* yield* TxRef.set(ref1, 10)
* yield* Effect.tx(TxRef.set(ref2, 20))
* const sum = (yield* TxRef.get(ref1)) + (yield* TxRef.get(ref2))
* console.log(`Transaction sum: ${sum}`)
* }))
*
* console.log(`Final ref1: ${yield* TxRef.get(ref1)}`) // 10
* console.log(`Final ref2: ${yield* TxRef.get(ref2)}`) // 20
* })
* ```
*
* @category transactions
* @since 4.0.0
*/
export const tx = effect => withFiber(fiber => {
if (fiber.context.mapUnsafe.has(Transaction.key)) {
return effect;
}
// Create transaction state only at the outermost boundary
const state = {
journal: new Map(),
retry: false
};
let result;
return uninterruptibleMask(restore => flatMap(whileLoop({
while: () => !result,
body: constant(restore(effect).pipe(provideService(Transaction, state), tapCause(() => {
if (!state.retry) return void_;
return restore(awaitPendingTransaction(state));
}), exit)),
step(exit) {
if (state.retry || !isTransactionConsistent(state)) {
return clearTransaction(state);
}
if (Exit.isSuccess(exit)) {
commitTransaction(fiber, state);
} else {
clearTransaction(state);
}
result = exit;
}
}), () => result));
});
const isTransactionConsistent = state => {
for (const [ref, {
version
}] of state.journal) {
if (ref.version !== version) {
return false;
}
}
return true;
};
const awaitPendingTransaction = state => suspend(() => {
const key = {};
const refs = Array.from(state.journal.keys());
const clearPending = () => {
for (const clear of refs) {
clear.pending.delete(key);
}
};
return callback(resume => {
const onCall = () => {
clearPending();
resume(void_);
};
for (const ref of refs) {
ref.pending.set(key, onCall);
}
return sync(clearPending);
});
});
function commitTransaction(fiber, state) {
for (const [ref, {
value
}] of state.journal) {
if (value !== ref.value) {
ref.version = ref.version + 1;
ref.value = value;
}
for (const pending of ref.pending.values()) {
fiber.currentDispatcher.scheduleTask(pending, 0);
}
ref.pending.clear();
}
}
function clearTransaction(state) {
state.retry = false;
state.journal.clear();
}
/**
* Retries the current transaction by signaling that it must be retried.
*
* **Details**
*
* NOTE: the transaction retries on any change to transactional values (i.e. TxRef) accessed in its body.
*
* **Example** (Retrying transactions)
*
* ```ts
* import { Effect, TxRef } from "effect"
*
* const program = Effect.gen(function*() {
* // create a transactional reference
* const ref = yield* TxRef.make(0)
*
* // forks a fiber that increases the value of `ref` every 100 millis
* yield* Effect.forkChild(Effect.forever(
* // update to transactional value
* Effect.tx(TxRef.update(ref, (n) => n + 1)).pipe(Effect.delay("100 millis"))
* ))
*
* // the following will retry 10 times until the `ref` value is 10
* yield* Effect.tx(Effect.gen(function*() {
* const value = yield* TxRef.get(ref)
* if (value < 10) {
* yield* Effect.log(`retry due to value: ${value}`)
* return yield* Effect.txRetry
* }
* yield* Effect.log(`transaction done with value: ${value}`)
* }))
* })
*
* Effect.runPromise(program).catch(console.error)
* ```
*
* @category transactions
* @since 4.0.0
*/
export const txRetry = /*#__PURE__*/flatMap(Transaction, state => {
state.retry = true;
return interrupt;
});
/**
* Converts an error-first callback API into a function that returns an
* `Effect`.
*
* **Details**
*
* The original function is called with the supplied arguments plus a final
* callback. A non-null callback error fails the returned effect, while a
* successful callback value becomes the effect success. Use `onError` to map
* callback errors and `onSyncError` to turn synchronous throws into typed
* failures; otherwise synchronous throws become defects.
*
* **Example** (Converting callbacks to effects)
*
* ```ts
* import { Effect } from "effect"
* import * as fs from "fs"
*
* // Convert Node.js readFile to an Effect
* const readFile = Effect.effectify(fs.readFile)
*
* // Use the effectified function
* const program = readFile("package.json", "utf8")
*
* Effect.runPromise(program).then(console.log)
* // Output: contents of package.json
* ```
*
* **Example** (Mapping callback errors to typed failures)
*
* ```ts
* import { Effect } from "effect"
* import * as fs from "fs"
*
* const readFile = Effect.effectify(
* fs.readFile,
* (error, args) => new Error(`Failed to read file ${args[0]}: ${error.message}`)
* )
*
* const program = readFile("nonexistent.txt", "utf8")
*
* Effect.runPromiseExit(program).then(console.log)
* // Output: Exit.failure with custom error message
* ```
*
* @category effectify
* @since 4.0.0
*/
export const effectify = (fn, onError, onSyncError) => (...args) => callback(resume => {
try {
fn(...args, (err, result) => {
if (err) {
resume(fail(onError ? onError(err, args) : err));
} else {
resume(succeed(result));
}
});
} catch (err) {
resume(onSyncError ? fail(onSyncError(err, args)) : die(err));
}
});
// -----------------------------------------------------------------------------
// Type constraints
// -----------------------------------------------------------------------------
/**
* Ensures that an effect's success type extends a given type `A`.
*
* **Details**
*
* This helper is checked at compile time and does not change the effect's
* runtime behavior.
*
* **Example** (Constraining the success type)
*
* ```ts
* import { Effect } from "effect"
*
* // Define a constraint that the success type must be a number
* const satisfiesNumber = Effect.satisfiesSuccessType<number>()
*
* // This works - Effect<42, never, never> extends Effect<number, never, never>
* const validEffect = satisfiesNumber(Effect.succeed(42))
*
* // This would cause a TypeScript compilation error:
* // const invalidEffect = satisfiesNumber(Effect.succeed("string"))
* // ^^^^^^^^^^^^^^^^^^^^^^
* // Type 'string' is not assignable to type 'number'
* ```
*
* @category utility types
* @since 4.0.0
*/
export const satisfiesSuccessType = () => effect => effect;
/**
* Ensures that an effect's error type extends a given type `E`.
*
* **Details**
*
* This helper is checked at compile time and does not change the effect's
* runtime behavior.
*
* **Example** (Constraining the error type)
*
* ```ts
* import { Data, Effect } from "effect"
*
* class ValidationError extends Data.TaggedError("ValidationError")<{}> {}
*
* // Define a constraint that the error type must be a ValidationError
* const satisfiesError = Effect.satisfiesErrorType<ValidationError>()
*
* // This works - Effect<number, ValidationError, never> extends the constrained type
* const validEffect = satisfiesError(Effect.fail(new ValidationError()))
*
* // This would cause a TypeScript compilation error:
* // const invalidEffect = satisfiesError(Effect.fail("string error"))
* // ^^^^^^^^^^^^^^^^^^^^^^^^^^^
* // Type 'string' is not assignable to type 'ValidationError'
* ```
*
* @category utility types
* @since 4.0.0
*/
export const satisfiesErrorType = () => effect => effect;
/**
* Ensures that an effect's requirements type extends a given type `R`.
*
* **Details**
*
* This helper is checked at compile time and does not change the effect's
* runtime behavior.
*
* **Example** (Constraining the services type)
*
* ```ts
* import { Effect } from "effect"
*
* // Define a constraint that requires a string as the requirements type
* const satisfiesStringServices = Effect.satisfiesServicesType<string>()
*
* // This works - effect requires string
* const validEffect: Effect.Effect<number, never, "config"> = Effect.succeed(42)
* const constrainedEffect = satisfiesStringServices(validEffect)
*
* // This would cause a TypeScript compilation error if uncommented:
* // const invalidEffect: Effect.Effect<number, never, number> = Effect.succeed(42)
* // const constrainedInvalid = satisfiesStringServices(invalidEffect)
* ```
*
* @category utility types
* @since 4.0.0
*/
export const satisfiesServicesType = () => effect => effect;
/**
* Applies `map` eagerly when an effect is already resolved.
*
* **When to use**
*
* Use when an already-resolved effect should apply a success transformation
* immediately while pending effects still use regular mapping.
*
* **Details**
*
* Success effects apply the mapping function immediately. Failure effects pass
* through unchanged, and pending effects fall back to regular `map` behavior.
*
* **Example** (Mapping already completed effects)
*
* ```ts
* import { Effect } from "effect"
*
* // For resolved effects, the mapping is applied immediately
* const resolved = Effect.succeed(5)
* const mapped = Effect.mapEager(resolved, (n) => n * 2) // Applied eagerly
*
* // For pending effects, behaves like regular map
* const pending = Effect.delay(Effect.succeed(5), "100 millis")
* const mappedPending = Effect.mapEager(pending, (n) => n * 2) // Uses regular map
* ```
*
* @category eager
* @since 4.0.0
*/
export const mapEager = internal.mapEager;
/**
* Applies `mapError` eagerly when an effect is already resolved.
*
* **When to use**
*
* Use when an already-resolved failed effect should apply an error
* transformation immediately while pending effects still use regular error
* mapping.
*
* **Details**
*
* Success effects pass through unchanged because there is no error to
* transform. Failure effects apply the mapping function immediately, and
* pending effects fall back to regular `mapError` behavior.
*
* **Example** (Mapping errors eagerly when possible)
*
* ```ts
* import { Effect } from "effect"
*
* // For resolved failure effects, the error mapping is applied immediately
* const failed = Effect.fail("original error")
* const mapped = Effect.mapErrorEager(failed, (err: string) => `mapped: ${err}`) // Applied eagerly
*
* // For pending effects, behaves like regular mapError
* const pending = Effect.delay(Effect.fail("error"), "100 millis")
* const mappedPending = Effect.mapErrorEager(
* pending,
* (err: string) => `mapped: ${err}`
* ) // Uses regular mapError
* ```
*
* @category eager
* @since 4.0.0
*/
export const mapErrorEager = internal.mapErrorEager;
/**
* Applies `mapBoth` eagerly when an effect is already resolved.
*
* **When to use**
*
* Use when an already-resolved effect should transform either success or
* failure immediately while pending effects still use regular channel mapping.
*
* **Details**
*
* Success effects apply `onSuccess` immediately, and failure effects apply
* `onFailure` immediately. Pending effects fall back to regular `mapBoth`
* behavior.
*
* **Example** (Mapping both channels eagerly when possible)
*
* ```ts
* import { Effect } from "effect"
*
* // For resolved effects, the appropriate mapping is applied immediately
* const success = Effect.succeed(5)
* const mapped = Effect.mapBothEager(success, {
* onFailure: (err: string) => `Failed: ${err}`,
* onSuccess: (n: number) => n * 2
* }) // onSuccess applied eagerly
*
* const failure = Effect.fail("error")
* const mappedError = Effect.mapBothEager(failure, {
* onFailure: (err: string) => `Failed: ${err}`,
* onSuccess: (n: number) => n * 2
* }) // onFailure applied eagerly
* ```
*
* @category eager
* @since 4.0.0
*/
export const mapBothEager = internal.mapBothEager;
/**
* Applies `flatMap` eagerly when an effect is already resolved.
*
* **When to use**
*
* Use when an already-resolved successful effect should bind immediately to the
* next effect while pending effects still use regular flat mapping.
*
* **Details**
*
* Success effects apply the flatMap function immediately. Failure effects pass
* through unchanged, and pending effects fall back to regular `flatMap`
* behavior.
*
* **Example** (Flat mapping eagerly when possible)
*
* ```ts
* import { Effect } from "effect"
*
* // For resolved effects, the flatMap is applied immediately
* const resolved = Effect.succeed(5)
* const flatMapped = Effect.flatMapEager(resolved, (n) => Effect.succeed(n * 2)) // Applied eagerly
*
* // For pending effects, behaves like regular flatMap
* const pending = Effect.delay(Effect.succeed(5), "100 millis")
* const flatMappedPending = Effect.flatMapEager(
* pending,
* (n) => Effect.succeed(n * 2)
* ) // Uses regular flatMap
* ```
*
* @category eager
* @since 4.0.0
*/
export const flatMapEager = internal.flatMapEager;
/**
* Applies `catch` eagerly when an effect is already resolved.
*
* **When to use**
*
* Use when an already-resolved failed effect should recover immediately while
* pending effects still use regular error recovery.
*
* **Details**
*
* Success effects pass through unchanged because there is no error to catch.
* Failure effects apply the catch function immediately, and pending effects
* fall back to regular `catch` behavior.
*
* **Example** (Catching failures eagerly when possible)
*
* ```ts
* import { Effect } from "effect"
*
* // For resolved failure effects, the catch function is applied immediately
* const failed = Effect.fail("original error")
* const recovered = Effect.catchEager(
* failed,
* (err: string) => Effect.succeed(`recovered from: ${err}`)
* ) // Applied eagerly
*
* // For success effects, returns success as-is
* const success = Effect.succeed(42)
* const unchanged = Effect.catchEager(
* success,
* (err: string) => Effect.succeed(`recovered from: ${err}`)
* ) // Returns success as-is
*
* // For pending effects, behaves like regular catch
* const pending = Effect.delay(Effect.fail("error"), "100 millis")
* const recoveredPending = Effect.catchEager(
* pending,
* (err: string) => Effect.succeed(`recovered from: ${err}`)
* ) // Uses regular catch
* ```
*
* @category eager
* @since 4.0.0
*/
export const catchEager = internal.catchEager;
/**
* Creates untraced function effects with eager evaluation optimization.
*
* **Details**
*
* Executes generator functions eagerly when all yielded effects are synchronous,
* stopping at the first async effect and deferring to normal execution.
*
* **Example** (Defining eager untraced effect functions)
*
* ```ts
* import { Effect } from "effect"
*
* const computation = Effect.fnUntracedEager(function*() {
* yield* Effect.succeed(1)
* yield* Effect.succeed(2)
* return "computed eagerly"
* })
*
* const effect = computation() // Executed immediately if all effects are sync
* ```
*
* @category eager
* @since 4.0.0
*/
export const fnUntracedEager = internal.fnUntracedEager;
//# sourceMappingURL=Effect.js.map

Xet Storage Details

Size:
245 kB
·
Xet hash:
387fb851aa384b9a7595614070dc6b66048830dd2fd70cb8969d47bde30ce389

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.