effect
Version:
The missing standard library for TypeScript, for writing production-grade software.
1,650 lines (1,649 loc) • 334 kB
JavaScript
import { dual } from "./Function.js";
import * as internalCause from "./internal/cause.js";
import * as console_ from "./internal/console.js";
import { TagProto } from "./internal/context.js";
import * as effect from "./internal/core-effect.js";
import * as core from "./internal/core.js";
import * as defaultServices from "./internal/defaultServices.js";
import * as circular from "./internal/effect/circular.js";
import * as internalExecutionPlan from "./internal/executionPlan.js";
import * as fiberRuntime from "./internal/fiberRuntime.js";
import * as layer from "./internal/layer.js";
import * as option_ from "./internal/option.js";
import * as query from "./internal/query.js";
import * as runtime_ from "./internal/runtime.js";
import * as schedule_ from "./internal/schedule.js";
import * as internalTracer from "./internal/tracer.js";
import * as Random from "./Random.js";
import * as Request from "./Request.js";
import * as Scheduler from "./Scheduler.js";
import { isGeneratorFunction } from "./Utils.js";
/**
* @since 2.0.0
* @category Symbols
*/
export const EffectTypeId = core.EffectTypeId;
/**
* Checks if a given value is an `Effect` value.
*
* **When to Use**
*
* This function can be useful for checking the type of a value before
* attempting to operate on it as an `Effect` value. For example, you could use
* `Effect.isEffect` to check the type of a value before using it as an argument
* to a function that expects an `Effect` value.
*
* @since 2.0.0
* @category Guards
*/
export const isEffect = core.isEffect;
/**
* Returns an effect that caches its result for a specified `Duration`,
* known as "timeToLive" (TTL).
*
* **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.
*
* **When to Use**
*
* Use this function when you have an effect that involves costly operations or
* computations, and you want to avoid repeating them within a short time frame.
*
* It's ideal for scenarios where the result of an effect doesn't change
* frequently and can be reused for a specified duration.
*
* By caching the result, you can improve efficiency and reduce unnecessary
* computations, especially in performance-critical applications.
*
* **Example**
*
* ```ts
* import { Effect, Console } 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.
*
* @since 2.0.0
* @category Caching
*/
export const cachedWithTTL = circular.cached;
/**
* Caches an effect's result for a specified duration and allows manual
* invalidation 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.
*
* **When to Use**
*
* Use this function when you have an effect whose result needs to be cached for
* a certain period, but you also want the option to refresh the cache manually
* before the expiration time.
*
* This is useful when you need to ensure that the cached data remains valid for
* a certain period but still want to invalidate it if the underlying data
* changes or if you want to force a recomputation.
*
* **Example**
*
* ```ts
* import { Effect, Console } 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.
*
* @since 2.0.0
* @category Caching
*/
export const cachedInvalidateWithTTL = circular.cachedInvalidateWithTTL;
/**
* Returns an effect that lazily computes a result and caches it for subsequent
* evaluations.
*
* **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.
*
* **When to Use**
*
* Use this function when you have an expensive or time-consuming operation that
* you want to avoid repeating. The first evaluation will compute the result,
* and all following evaluations will immediately return the cached value,
* improving performance and reducing unnecessary work.
*
* **Example**
*
* ```ts
* import { Effect, Console } 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.
*
* @since 2.0.0
* @category Caching
*/
export const cached = effect.memoize;
/**
* Returns a memoized version of a function with effects, reusing results for
* the same inputs.
*
* **Details**
*
* This function creates a memoized version of a given function that performs an
* effect. Memoization ensures that once a result is computed for a specific
* input, it is stored and reused for subsequent calls with the same input,
* reducing the need to recompute the result.
*
* The function can optionally take an `Equivalence` parameter to
* determine how inputs are compared for caching purposes.
*
* **When to Use**
*
* Use this function when you have a function that performs an effect and you
* want to avoid recomputing the result for the same input multiple times.
*
* It's ideal for functions that produce deterministic results based on their
* inputs, and you want to improve performance by caching the output.
*
* This is particularly useful in scenarios where the function involves
* expensive calculations or operations that should be avoided after the first
* execution with the same parameters.
*
* **Example**
*
* ```ts
* import { Effect, Random } from "effect"
*
* const program = Effect.gen(function* () {
* const randomNumber = (n: number) => Random.nextIntBetween(1, n)
* console.log("non-memoized version:")
* console.log(yield* randomNumber(10))
* console.log(yield* randomNumber(10))
*
* console.log("memoized version:")
* const memoized = yield* Effect.cachedFunction(randomNumber)
* console.log(yield* memoized(10))
* console.log(yield* memoized(10))
* })
*
* Effect.runFork(program)
* // Example Output:
* // non-memoized version:
* // 2
* // 8
* // memoized version:
* // 5
* // 5
* ```
*
* @since 2.0.0
* @category Caching
*/
export const cachedFunction = circular.cachedFunction;
/**
* Returns an effect that executes only once, regardless of how many times it's
* called.
*
* **Details**
*
* This function ensures that a specific effect is executed only a single time,
* no matter how many times it is invoked. The result of the effect will be
* cached, and subsequent calls to the effect will immediately return the cached
* result without re-executing the original logic.
*
* **When to Use**
*
* Use this function when you need to perform a task only once, regardless of
* how many times the effect is triggered. It's particularly useful when you
* have initialization tasks, logging, or other one-time actions that should not
* be repeated. This can help optimize performance and avoid redundant actions.
*
* **Example**
*
* ```ts
* import { Effect, Console } from "effect"
*
* const program = Effect.gen(function* () {
* const task1 = Console.log("task1")
* yield* Effect.repeatN(task1, 2)
* const task2 = yield* Effect.once(Console.log("task2"))
* yield* Effect.repeatN(task2, 2)
* })
*
* Effect.runFork(program)
* // Output:
* // task1
* // task1
* // task1
* // task2
* ```
*
* @since 2.0.0
* @category Caching
*/
export const once = effect.once;
/**
* Combines multiple effects into one, returning results based on the input
* structure.
*
* **Details**
*
* Use this function when you need to run multiple effects and combine their
* results into a single output. It supports tuples, iterables, structs, and
* records, making it flexible for different input types.
*
* For instance, if the input is a tuple:
*
* ```ts skip-type-checking
* // ┌─── a tuple of effects
* // ▼
* Effect.all([effect1, effect2, ...])
* ```
*
* the effects are executed sequentially, and the result is a new effect
* containing the results as a tuple. The results in the tuple match the order
* of the effects passed to `Effect.all`.
*
* **Concurrency**
*
* You can control the execution order (e.g., sequential vs. concurrent) using
* the `concurrency` option.
*
* **Short-Circuiting Behavior**
*
* This function stops execution on the first error it encounters, this is
* called "short-circuiting". If any effect in the collection fails, the
* remaining effects will not run, and the error will be propagated. To change
* this behavior, you can use the `mode` option, which allows all effects to run
* and collect results as `Either` or `Option`.
*
* **The `mode` option**
*
* The `{ mode: "either" }` option changes the behavior of `Effect.all` to
* ensure all effects run, even if some fail. Instead of stopping on the first
* failure, this mode collects both successes and failures, returning an array
* of `Either` instances where each result is either a `Right` (success) or a
* `Left` (failure).
*
* Similarly, the `{ mode: "validate" }` option uses `Option` to indicate
* success or failure. Each effect returns `None` for success and `Some` with
* the error for failure.
*
* **Example** (Combining Effects in Tuples)
*
* ```ts
* import { Effect, Console } 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** (Combining Effects in Iterables)
*
* ```ts
* import { Effect, Console } 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** (Combining Effects in Structs)
*
* ```ts
* import { Effect, Console } 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** (Combining Effects in Records)
*
* ```ts
* import { Effect, Console } 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** (Short-Circuiting Behavior)
*
* ```ts
* import { Effect, Console } 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!' }
* // }
* ```
*
* **Example** (Collecting Results with `mode: "either"`)
*
* ```ts
* import { Effect, Console } from "effect"
*
* const effects = [
* Effect.succeed("Task1").pipe(Effect.tap(Console.log)),
* Effect.fail("Task2: Oh no!").pipe(Effect.tap(Console.log)),
* Effect.succeed("Task3").pipe(Effect.tap(Console.log))
* ]
*
* const program = Effect.all(effects, { mode: "either" })
*
* Effect.runPromiseExit(program).then(console.log)
* // Output:
* // Task1
* // Task3
* // {
* // _id: 'Exit',
* // _tag: 'Success',
* // value: [
* // { _id: 'Either', _tag: 'Right', right: 'Task1' },
* // { _id: 'Either', _tag: 'Left', left: 'Task2: Oh no!' },
* // { _id: 'Either', _tag: 'Right', right: 'Task3' }
* // ]
* // }
* ```
*
* **Example** (Collecting Results with `mode: "validate"`)
*
* ```ts
* import { Effect, Console } from "effect"
*
* const effects = [
* Effect.succeed("Task1").pipe(Effect.tap(Console.log)),
* Effect.fail("Task2: Oh no!").pipe(Effect.tap(Console.log)),
* Effect.succeed("Task3").pipe(Effect.tap(Console.log))
* ]
*
* const program = Effect.all(effects, { mode: "validate" })
*
* Effect.runPromiseExit(program).then((result) => console.log("%o", result))
* // Output:
* // Task1
* // Task3
* // {
* // _id: 'Exit',
* // _tag: 'Failure',
* // cause: {
* // _id: 'Cause',
* // _tag: 'Fail',
* // failure: [
* // { _id: 'Option', _tag: 'None' },
* // { _id: 'Option', _tag: 'Some', value: 'Task2: Oh no!' },
* // { _id: 'Option', _tag: 'None' }
* // ]
* // }
* // }
* ```
*
* @see {@link forEach} for iterating over elements and applying an effect.
* @see {@link allWith} for a data-last version of this function.
*
* @since 2.0.0
* @category Collecting
*/
export const all = fiberRuntime.all;
/**
* A data-last version of {@link all}, designed for use in pipelines.
*
* **When to Use**
*
* This function enables you to combine multiple effects and customize execution
* options such as concurrency levels. This version is useful in functional
* pipelines where you first define your data and then apply operations to it.
*
* **Example**
*
* ```ts
* import { Effect, pipe } 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 program = pipe(
* [task1, task2],
* // Run both effects concurrently using the concurrent option
* Effect.allWith({ concurrency: 2 })
* )
*
* Effect.runPromise(program).then(console.log)
* // Output:
* // timestamp=... level=INFO fiber=#3 message="task2 done"
* // timestamp=... level=INFO fiber=#2 message="task1 done"
* // [ 1, 'hello' ]
* ```
*
* @since 2.0.0
* @category Collecting
*/
export const allWith = fiberRuntime.allWith;
/**
* Evaluates and runs each effect in the iterable, collecting only the
* successful results while discarding failures.
*
* **Details**
*
* This function function processes an iterable of effects and runs each one. If
* an effect is successful, its result is collected; if it fails, the result is
* discarded. This ensures that only successful outcomes are kept.
*
* **Options**
*
* The function also allows you to customize how the effects are handled by
* specifying options such as concurrency, batching, and how finalizers behave.
* These options provide flexibility in running the effects concurrently or
* adjusting other execution details.
*
* **Example**
*
* ```ts
* import { Effect } from "effect"
*
* const tasks = [
* Effect.succeed(1),
* Effect.fail("Error 1"),
* Effect.succeed(2),
* Effect.fail("Error 2")
* ]
*
* const program = Effect.gen(function*() {
* const successfulResults = yield* Effect.allSuccesses(tasks)
* console.log(successfulResults)
* })
*
* Effect.runFork(program)
* // Output: [1, 2]
*
* ```
*
* @since 2.0.0
* @category Collecting
*/
export const allSuccesses = fiberRuntime.allSuccesses;
/**
* Drops elements until the effectful predicate returns `true`.
*
* **Details**
*
* This function processes a collection of elements and uses an effectful
* predicate to determine when to stop dropping elements. It drops elements from
* the beginning of the collection until the predicate returns `true`.
*
* The predicate is a function that takes an element and its index in the
* collection and returns an effect that evaluates to a boolean.
*
* Once the predicate returns `true`, the remaining elements of the collection
* are returned.
*
* **Note**: The first element for which the predicate returns `true` is also
* dropped.
*
* **When to Use**
*
* This function allows you to conditionally skip over a part of the collection
* based on some criteria defined in the predicate.
*
* **Example**
*
* ```ts
* import { Effect } from "effect"
*
* const numbers = [1, 2, 3, 4, 5, 6]
* const predicate = (n: number, i: number) => Effect.succeed(n > 3)
*
* const program = Effect.gen(function*() {
* const result = yield* Effect.dropUntil(numbers, predicate)
* console.log(result)
* })
*
* Effect.runFork(program)
* // Output: [5, 6]
* ```
*
* @see {@link dropWhile} for a similar function that drops elements while the
* predicate returns `true`.
*
* @since 2.0.0
* @category Collecting
*/
export const dropUntil = effect.dropUntil;
/**
* Drops elements as long as the predicate returns `true`.
*
* **Details**
*
* This function processes a collection of elements and uses a predicate to
* decide whether to drop an element.
*
* The predicate is a function that takes an element and its index, and it
* returns an effect that evaluates to a boolean.
*
* As long as the predicate returns `true`, elements will continue to be dropped
* from the collection.
*
* Once the predicate returns `false`, the remaining elements are kept.
*
* **When to Use**
*
* This function allows you to discard elements from the start of a collection
* based on a condition, and only keep the rest when the condition no longer
* holds.
*
* **Example**
*
* ```ts
* import { Effect } from "effect"
*
* const numbers = [1, 2, 3, 4, 5, 6]
* const predicate = (n: number, i: number) => Effect.succeed(n <= 3)
*
* const program = Effect.gen(function*() {
* const result = yield* Effect.dropWhile(numbers, predicate)
* console.log(result)
* })
*
* Effect.runFork(program)
* // Output: [4, 5, 6]
* ```
*
* @see {@link dropUntil} for a similar function that drops elements until the
* predicate returns `true`.
*
* @since 2.0.0
* @category Collecting
*/
export const dropWhile = effect.dropWhile;
/**
* Takes elements from a collection until the effectful predicate returns
* `true`.
*
* **Details**
*
* This function processes a collection of elements and uses an effectful
* predicate to decide when to stop taking elements. The elements are taken from
* the beginning of the collection until the predicate returns `true`.
*
* The predicate is a function that takes an element and its index in the
* collection, and returns an effect that resolves to a boolean.
*
* Once the predicate returns `true`, the remaining elements of the collection
* are discarded, and the function stops taking more elements.
*
* **Note**: The first element for which the predicate returns `true` is also
* included in the result.
*
* **When to Use**
*
* Use this function when you want to conditionally take elements from a
* collection based on a dynamic condition. For example, you may want to collect
* numbers from a list until a certain threshold is reached, or gather items
* until a specific condition is met.
*
* **Example**
*
* ```ts
* import { Effect } from "effect"
*
* const numbers = [1, 2, 3, 4, 5, 6]
* const predicate = (n: number, i: number) => Effect.succeed(n > 3)
*
* const program = Effect.gen(function*() {
* const result = yield* Effect.takeUntil(numbers, predicate)
* console.log(result)
* })
*
* Effect.runFork(program)
* // Output: [ 1, 2, 3, 4 ]
* ```
*
* @see {@link takeWhile} for a similar function that takes elements while the
* predicate returns `true`.
*
* @since 2.0.0
* @category Collecting
*/
export const takeUntil = effect.takeUntil;
/**
* Takes elements as long as the predicate returns `true`.
*
* **Details**
*
* This function processes a collection of elements and uses a predicate to
* decide whether to take an element.
*
* The predicate is a function that takes an element and its index, and it
* returns an effect that evaluates to a boolean.
*
* As long as the predicate returns `true`, elements will continue to be taken
* from the collection.
*
* Once the predicate returns `false`, the remaining elements are discarded.
*
* **Example**
*
* ```ts
* import { Effect } from "effect"
*
* const numbers = [1, 2, 3, 4, 5, 6]
* const predicate = (n: number, i: number) => Effect.succeed(n <= 3)
*
* const program = Effect.gen(function*() {
* const result = yield* Effect.takeWhile(numbers, predicate)
* console.log(result)
* })
*
* Effect.runFork(program)
* // Output: [1, 2, 3]
* ```
*
* @see {@link takeUntil} for a similar function that takes elements until the predicate returns `true`.
*
* @since 2.0.0
* @category Collecting
*/
export const takeWhile = effect.takeWhile;
/**
* Determines whether all elements of the iterable satisfy the effectful
* predicate.
*
* **Details**
*
* This function checks whether every element in a given collection (an
* iterable) satisfies a condition defined by an effectful predicate.
*
* The predicate is a function that takes an element and its index, and it
* returns an effect that evaluates to a boolean.
*
* The function will process each element and return `true` if all elements
* satisfy the predicate; otherwise, it returns `false`.
*
* **When to Use**
*
* This function is useful when you need to verify that all items in a
* collection meet certain criteria, even when the evaluation of each item
* involves effects, such as asynchronous checks or complex computations.
*
* **Example**
*
* ```ts
* import { Effect } from "effect"
*
* const numbers = [2, 4, 6, 8]
* const predicate = (n: number, i: number) => Effect.succeed(n % 2 === 0)
*
* const program = Effect.gen(function*() {
* const allEven = yield* Effect.every(numbers, predicate)
* console.log(allEven)
* })
*
* Effect.runFork(program)
* // Output: true
* ```
*
* @see {@link exists} for a similar function that returns a boolean indicating
* whether **any** element satisfies the predicate.
*
* @since 2.0.0
* @category Condition Checking
*/
export const every = effect.every;
/**
* Determines whether any element of the iterable satisfies the effectual
* predicate.
*
* **Details**
*
* This function checks whether any element in a given collection (an iterable)
* satisfies a condition defined by an effectful predicate.
*
* The predicate is a function that takes an element and its index, and it
* returns an effect that evaluates to a boolean.
*
* The function will process each element, and if any element satisfies the
* predicate (returns `true`), the function will immediately return `true`.
*
* If none of the elements satisfy the condition, it will return `false`.
*
* **When to Use**
*
* This function allows you to quickly check for a condition in a collection
* without having to manually iterate over it.
*
* **Example**
*
* ```ts
* import { Effect } from "effect"
*
* const numbers = [1, 2, 3, 4]
* const predicate = (n: number, i: number) => Effect.succeed(n > 2)
*
* const program = Effect.gen(function*() {
* const hasLargeNumber = yield* Effect.exists(numbers, predicate)
* console.log(hasLargeNumber)
* })
*
* Effect.runFork(program)
* // Output: true
* ```
*
* @see {@link every} for a similar function that checks if **all** elements
* satisfy the predicate.
*
* @since 2.0.0
* @category Condition Checking
*/
export const exists = fiberRuntime.exists;
/**
* Filters an iterable using the specified effectful predicate.
*
* **Details**
*
* This function filters a collection (an iterable) by applying an effectful
* predicate.
*
* The predicate is a function that takes an element and its index, and it
* returns an effect that evaluates to a boolean.
*
* The function processes each element in the collection and keeps only those
* that satisfy the condition defined by the predicate.
*
* **Options**
*
* You can also adjust the behavior with options such as concurrency, batching,
* or whether to negate the condition.
*
* **When to Use**
*
* This function allows you to selectively keep or remove elements based on a
* condition that may involve asynchronous or side-effect-causing operations.
*
* **Example**
*
* ```ts
* import { Effect } from "effect"
*
* const numbers = [1, 2, 3, 4, 5]
* const predicate = (n: number, i: number) => Effect.succeed(n % 2 === 0)
*
* const program = Effect.gen(function*() {
* const result = yield* Effect.filter(numbers, predicate)
* console.log(result)
* })
*
* Effect.runFork(program)
* // Output: [2, 4]
* ```
*
* @since 2.0.0
* @category Filtering
*/
export const filter = fiberRuntime.filter;
/**
* Filters and maps elements sequentially in one operation.
*
* This function processes each element one by one. It applies a function that
* returns an `Option` to each element. If the function returns `Some`, the
* element is kept; if it returns `None`, the element is removed. The operation
* is done sequentially for each element.
*
* **Example**
*
* ```ts
* import { Console, Effect, Option } from "effect"
*
* const task = (n: number) =>
* Effect.succeed(n).pipe(
* Effect.delay(1000 - (n * 100)),
* Effect.tap(Console.log(`task${n} done`))
* )
*
* const program = Effect.filterMap(
* [task(1), task(2), task(3), task(4)],
* (n) => n % 2 === 0 ? Option.some(n) : Option.none()
* )
*
* Effect.runPromise(program).then(console.log)
* // Output:
* // task1 done
* // task2 done
* // task3 done
* // task4 done
* // [ 2, 4 ]
* ```
*
* @since 2.0.0
* @category Filtering
*/
export const filterMap = effect.filterMap;
/**
* Returns the first element that satisfies the effectful predicate.
*
* **Details**
*
* This function processes a collection of elements and applies an effectful
* predicate to each element.
*
* The predicate is a function that takes an element and its index in the
* collection, and it returns an effect that evaluates to a boolean.
*
* The function stops as soon as it finds the first element for which the
* predicate returns `true` and returns that element wrapped in an `Option`.
*
* If no element satisfies the predicate, the result will be `None`.
*
* **When to Use**
*
* This function allows you to efficiently find an element that meets a specific
* condition, even when the evaluation involves effects like asynchronous
* operations or side effects.
*
* **Example**
*
* ```ts
* import { Effect } from "effect"
*
* const numbers = [1, 2, 3, 4, 5]
* const predicate = (n: number, i: number) => Effect.succeed(n > 3)
*
* const program = Effect.gen(function*() {
* const result = yield* Effect.findFirst(numbers, predicate)
* console.log(result)
* })
*
* Effect.runFork(program)
* // Output: { _id: 'Option', _tag: 'Some', value: 4 }
* ```
*
* @since 2.0.0
* @category Collecting
*/
export const findFirst = effect.findFirst;
/**
* Executes an effectful operation for each element in an `Iterable`.
*
* **Details**
*
* This 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** (Applying Effects to Iterable Elements)
*
* ```ts
* import { Effect, Console } 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** (Discarding Results)
*
* ```ts
* import { Effect, Console } 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.
*
* @since 2.0.0
* @category Looping
*/
export const forEach = fiberRuntime.forEach;
/**
* Returns the first element of the iterable if the collection is non-empty, or
* fails with the error `NoSuchElementException` if the collection is empty.
*
* **When to Use**
*
* This function is useful when you need to retrieve the first item from a
* collection and want to handle the case where the collection might be empty
* without causing an unhandled exception.
*
* **Example**
*
* ```ts
* import { Effect } from "effect"
*
* // Simulate an async operation
* const fetchNumbers = Effect.succeed([1, 2, 3]).pipe(Effect.delay("100 millis"))
*
* const program = Effect.gen(function*() {
* const firstElement = yield* Effect.head(fetchNumbers)
* console.log(firstElement)
* })
*
* Effect.runFork(program)
* // Output: 1
* ```
*
* @since 2.0.0
* @category Collecting
*/
export const head = effect.head;
/**
* Merges an `Iterable<Effect<A, E, R>>` to a single effect.
*
* **Details**
*
* This function takes an iterable of effects and combines them into a single
* effect. It does this by iterating over each effect in the collection and
* applying a function that accumulates results into a "zero" value, which
* starts with an initial value and is updated with each effect's success.
*
* The provided function `f` is called for each element in the iterable,
* allowing you to specify how to combine the results.
*
* **Options**
*
* The function also allows you to customize how the effects are handled by
* specifying options such as concurrency, batching, and how finalizers behave.
* These options provide flexibility in running the effects concurrently or
* adjusting other execution details.
*
* **Example**
*
* ```ts
* import { Effect } from "effect"
*
* const numbers = [Effect.succeed(1), Effect.succeed(2), Effect.succeed(3)]
* const add = (sum: number, value: number, i: number) => sum + value
* const zero = 0
*
* const program = Effect.gen(function*() {
* const total = yield* Effect.mergeAll(numbers, zero, add)
* console.log(total)
* })
*
* Effect.runFork(program)
* // Output: 6
* ```
*
* @since 2.0.0
* @category Collecting
*/
export const mergeAll = fiberRuntime.mergeAll;
/**
* Processes an iterable and applies an effectful function to each element,
* categorizing the results into successes and failures.
*
* **Details**
*
* This function processes each element in the provided iterable by applying an
* effectful function to it. The results are then categorized into two separate
* lists: one for failures and another for successes. This separation allows you
* to handle the two categories differently. Failures are collected in a list
* without interrupting the processing of the remaining elements, so the
* operation continues even if some elements fail. This is particularly useful
* when you need to handle both successful and failed results separately,
* without stopping the entire process on encountering a failure.
*
* **When to Use**
*
* Use this function when you want to process a collection of items and handle
* errors or failures without interrupting the processing of other items. It's
* useful when you need to distinguish between successful and failed results and
* process them separately, for example, when logging errors while continuing to
* work with valid data. The function ensures that failures are captured, while
* successes are processed normally.
*
* **Example**
*
* ```ts
* import { Effect } from "effect"
*
* // ┌─── Effect<[string[], number[]], never, never>
* // ▼
* const program = Effect.partition([0, 1, 2, 3, 4], (n) => {
* if (n % 2 === 0) {
* return Effect.succeed(n)
* } else {
* return Effect.fail(`${n} is not even`)
* }
* })
*
* Effect.runPromise(program).then(console.log, console.error)
* // Output:
* // [ [ '1 is not even', '3 is not even' ], [ 0, 2, 4 ] ]
* ```
*
* @see {@link validateAll} for a function that either collects all failures or all successes.
* @see {@link validateFirst} for a function that stops at the first success.
*
* @since 2.0.0
* @category Error Accumulation
*/
export const partition = fiberRuntime.partition;
/**
* Reduces an `Iterable<A>` using an effectual function `f`, working
* sequentially from left to right.
*
* **Details**
*
* This function takes an iterable and applies a function `f` to each element in
* the iterable. The function works sequentially, starting with an initial value
* `zero` and then combining it with each element in the collection. The
* provided function `f` is called for each element in the iterable, allowing
* you to accumulate a result based on the current value and the element being
* processed.
*
* **When to Use**
*
* The function is often used for operations like summing a collection of
* numbers or combining results from multiple tasks. It ensures that operations
* are performed one after the other, maintaining the order of the elements.
*
* **Example**
*
* ```ts
* import { Console, Effect } from "effect"
*
* const processOrder = (id: number) =>
* Effect.succeed({ id, price: 100 * id })
* .pipe(Effect.tap(() => Console.log(`Order ${id} processed`)), Effect.delay(500 - (id * 100)))
*
* const program = Effect.reduce(
* [1, 2, 3, 4],
* 0,
* (acc, id, i) =>
* processOrder(id)
* .pipe(Effect.map((order) => acc + order.price))
* )
*
* Effect.runPromise(program).then(console.log)
* // Output:
* // Order 1 processed
* // Order 2 processed
* // Order 3 processed
* // Order 4 processed
* // 1000
* ```
*
* @see {@link reduceWhile} for a similar function that stops the process based on a predicate.
* @see {@link reduceRight} for a similar function that works from right to left.
*
* @since 2.0.0
* @category Collecting
*/
export const reduce = effect.reduce;
/**
* Reduces an `Iterable<A>` using an effectual function `body`, working
* sequentially from left to right, stopping the process early when the
* predicate `while` is not satisfied.
*
* **Details**
*
* This function processes a collection of elements, applying a function `body`
* to reduce them to a single value, starting from the first element. It checks
* the value of the accumulator against a predicate (`while`). If at any point
* the predicate returns `false`, the reduction stops, and the accumulated
* result is returned.
*
* **When to Use**
*
* Use this function when you need to reduce a collection of elements, but only
* continue the process as long as a certain condition holds true. For example,
* if you want to sum values in a list but stop as soon as the sum exceeds a
* certain threshold, you can use this function.
*
* **Example**
*
* ```ts
* import { Console, Effect } from "effect"
*
* const processOrder = (id: number) =>
* Effect.succeed({ id, price: 100 * id })
* .pipe(Effect.tap(() => Console.log(`Order ${id} processed`)), Effect.delay(500 - (id * 100)))
*
* const program = Effect.reduceWhile(
* [1, 2, 3, 4],
* 0,
* {
* body: (acc, id, i) =>
* processOrder(id)
* .pipe(Effect.map((order) => acc + order.price)),
* while: (acc) => acc < 500
* }
* )
*
* Effect.runPromise(program).then(console.log)
* // Output:
* // Order 1 processed
* // Order 2 processed
* // Order 3 processed
* // 600
* ```
*
* @since 2.0.0
* @category Collecting
*/
export const reduceWhile = effect.reduceWhile;
/**
* Reduces an `Iterable<A>` using an effectual function `f`, working
* sequentially from right to left.
*
* **Details**
*
* This function takes an iterable and applies a function `f` to each element in
* the iterable. The function works sequentially, starting with an initial value
* `zero` and then combining it with each element in the collection. The
* provided function `f` is called for each element in the iterable, allowing
* you to accumulate a result based on the current value and the element being
* processed.
*
* **When to Use**
*
* The function is often used for operations like summing a collection of
* numbers or combining results from multiple tasks. It ensures that operations
* are performed one after the other, maintaining the order of the elements.
*
* **Example**
*
* ```ts
* import { Console, Effect } from "effect"
*
* const processOrder = (id: number) =>
* Effect.succeed({ id, price: 100 * id })
* .pipe(Effect.tap(() => Console.log(`Order ${id} processed`)), Effect.delay(500 - (id * 100)))
*
* const program = Effect.reduceRight(
* [1, 2, 3, 4],
* 0,
* (id, acc, i) =>
* processOrder(id)
* .pipe(Effect.map((order) => acc + order.price))
* )
*
* Effect.runPromise(program).then(console.log)
* // Output:
* // Order 4 processed
* // Order 3 processed
* // Order 2 processed
* // Order 1 processed
* // 1000
* ```
*
* @see {@link reduce} for a similar function that works from left to right.
*
* @since 2.0.0
* @category Collecting
*/
export const reduceRight = effect.reduceRight;
/**
* Reduces an `Iterable<Effect<A, E, R>>` to a single effect.
*
* **Details**
*
* This function processes a collection of effects and combines them into one
* single effect. It starts with an initial effect (`zero`) and applies a
* function `f` to each element in the collection.
*
* **Options**
*
* The function also allows you to customize how the effects are handled by
* specifying options such as concurrency, batching, and how finalizers behave.
* These options provide flexibility in running the effects concurrently or
* adjusting other execution details.
*
* **Example**
*
* ```ts
* import { Console, Effect } from "effect"
*
* const processOrder = (id: number) =>
* Effect.succeed({ id, price: 100 * id })
* .pipe(Effect.tap(() => Console.log(`Order ${id} processed`)), Effect.delay(500 - (id * 100)))
*
* const program = Effect.reduceEffect(
* [processOrder(1), processOrder(2), processOrder(3), processOrder(4)],
* Effect.succeed(0),
* (acc, order, i) => acc + order.price
* )
*
* Effect.runPromise(program).then(console.log)
* // Output:
* // Order 1 processed
* // Order 2 processed
* // Order 3 processed
* // Order 4 processed
* // 1000
* ```
*
* @since 2.0.0
* @category Collecting
*/
export const reduceEffect = fiberRuntime.reduceEffect;
/**
* Replicates the given effect `n` times.
*
* **Details**
*
* This function takes an effect and replicates it a specified number of times
* (`n`). The result is an array of `n` effects, each of which is identical to
* the original effect.
*
* **Example**
*
* ```ts
* import { Console, Effect } from "effect"
*
* const task = Effect.succeed("Hello, World!").pipe(
* Effect.tap(Console.log)
* )
*
* const program = Effect.gen(function*() {
* // Replicate the task 3 times
* const tasks = Effect.replicate(task, 3)
* for (const t of tasks) {
* // Run each task
* yield* t
* }
* })
*
* Effect.runFork(program)
* // Output:
* // Hello, World!
* // Hello, World!
* // Hello, World!
* ```
*
* @since 2.0.0
*/
export const replicate = fiberRuntime.replicate;
/**
* Performs this effect the specified number of times and collects the results.
*
* **Details**
*
* This function repeats an effect multiple times and collects the results into
* an array. You specify how many times to execute the effect, and it runs that
* many times, either in sequence or concurrently depending on the provided
* options.
*
* **Options**
*
* If the `discard` option is set to `true`, the intermediate results are not
* collected, and the final result of the operation is `void`.
*
* The function also allows you to customize how the effects are handled by
* specifying options such as concurrency, batching, and how finalizers behave.
* These options provide flexibility in running the effects concurrently or
* adjusting other execution details.
*
* **Example**
*
* ```ts
* import { Console, Effect } from "effect"
*
* let counter = 0
*
* const task = Effect.sync(() => ++counter).pipe(
* Effect.tap(() => Console.log(`Task completed`))
* )
*
* const program = Effect.gen(function*() {
* // Replicate the task 3 times and collect the results
* const results = yield* Effect.replicateEffect(task, 3)
* yield* Console.log(`Results: ${results.join(", ")}`)
* })
*
* Effect.runFork(program)
* // Output:
* // Task completed
* // Task completed
* // Task completed
* // Results: 1, 2, 3
* ```
*
* @since 2.0.0
* @category Collecting
*/
export const replicateEffect = fiberRuntime.replicateEffect;
/**
* Applies an effectful operation to each element in a collection while
* collecting both successes and failures.
*
* **Details**
*
* This function allows you to apply an effectful operation to every item in a
* collection.
*
* Unlike {@link forEach}, which would stop at the first error, this function
* continues processing all elements, accumulating both successes and failures.
*
* **When to Use**
*
* Use this function when you want to process every item in a collection, even
* if some items fail. This is particularly useful when you need to perform
* operations on all elements without halting due to an error.
*
* Keep in mind that if there are any failures, **all successes will be lost**,
* so this function is not suitable when you need to keep the successful results
* in case of errors.
*
* **Example**
*
* ```ts
* import { Effect, Console } from "effect"
*
* // ┌─── Effect<number[], [string, ...string[]], never>
* // ▼
* const program = Effect.validateAll([1, 2, 3, 4, 5], (n) => {
* if (n < 4) {
* return Console.log(`item ${n}`).pipe(Effect.as(n))
* } else {
* return Effect.fail(`${n} is not less that 4`)
* }
* })
*
* Effect.runPromiseExit(program).then(console.log)
* // Output:
* // item 1
* // item 2
* // item 3
* // {
* // _id: 'Exit',
* // _tag: 'Failure',
* // cause: {
* // _id: 'Cause',
* // _tag: 'Fail',
* // failure: [ '4 is not less that 4', '5 is not less that 4' ]
* // }
* // }
* ```
*
* @see {@link forEach} for a similar function that stops at the first error.
* @see {@link partition} when you need to separate successes and failures
* instead of losing successes with errors.
*
* @since 2.0.0
* @category Error Accumulation
*/
export const validateAll = fiberRuntime.validateAll;
/**
* This function is similar to {@link validateAll} but with a key difference: it
* returns the first successful result or all errors if none of the operations
* succeed.
*
* **Details**
*
* This function processes a collection of elements and applies an effectful
* operation to each. Unlike {@link validateAll}, which accumulates both
* successes and failures, `Effect.validateFirst` stops and returns the first
* success it encounters. If no success occurs, it returns all accumulated
* errors. This can be useful when you are interested in the first successful
* result and want to avoid processing further once a valid result is found.
*
* **Example**
*
* ```ts
* import { Effect, Console } from "effect"
*
* // ┌─── Effect<number, string[], never>
* // ▼
* const program = Effect.validateFirst([1, 2, 3, 4, 5], (n) => {
* if (n < 4) {
* return Effect.fail(`${n} is not less that 4`)
* } else {
* return Console.log(`item ${n}`).pipe(Effect.as(n))
* }
* })
*
* Effect.runPromise(program).then(console.log, console.error)
* // Output:
* // item 4
* // 4
* ```
*
* @see {@link validateAll} for a similar function that accumulates all results.
* @see {@link firstSuccessOf} for a similar function that processes multiple
* effects and returns the first successful one or the last error.
*
* @since 2.0.0
* @category Error Accumulation
*/
export const validateFirst = fiberRuntime.validateFirst;
/**
* Creates an `Effect` from a callback-based asynchronous function.
*
* **Details**
*
* The `resume` function:
* - Must be called exactly once. Any additional calls will be ignored.
* - Can return an optional `Effect` that will be run if the `Fiber` executing
* this `Effect` is interrupted. This can be useful in scenarios where you
* need to handle resource cleanup if the operation is interrupted.
* - Can receive an `AbortSignal` to handle interruption if needed.
*
* The `FiberId` of the fiber that may complete the async callback may also be
* specified using the `blockingOn` argument. This is called the "blocking
* fiber" because it suspends the fiber executing the `async` effect (i.e.
* semantically blocks the fiber from making progress). Specifying this fiber id
* in cases where it is known will improve diagnostics, but not affect the
* behavior of the returned effect.
*
* **When to Use**
*
* Use `Effect.async` when dealing with APIs that use callback-style instead of
* `async/await` or `Promise`.