Skip to content

Layer Memoization

Layer memoization allows a layer to be created once and used multiple times in the dependency graph. If we use the same layer twice:

Layer.merge(Layer.provide(L2, L1), Layer.provide(L3, L1))

then the L1 layer will be allocated only once.

One important feature of an Effect application is that layers are shared by default. This means that if the same layer is used twice, and if we provide the layer globally, the layer will only be allocated a single time. For every layer in our dependency graph, there is only one instance of it that is shared between all the layers that depend on it.

Example

For example, assume we have the three services A, B, and C. The implementation of both B and C is dependent on the A service:

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Context

@since2.0.0

@since2.0.0

Context
,
import Layer
Layer
} from "effect"
class
class A
A
extends
import Context

@since2.0.0

@since2.0.0

Context
.
const Tag: <"A">(id: "A") => <Self, Shape>() => Context.TagClass<Self, "A", Shape>

@example

import { Context, Layer } from "effect"
class MyTag extends Context.Tag("MyTag")<
MyTag,
{ readonly myNum: number }
>() {
static Live = Layer.succeed(this, { myNum: 108 })
}

@since2.0.0

Tag
("A")<
class A
A
, { readonly
a: number
a
: number }>() {}
class
class B
B
extends
import Context

@since2.0.0

@since2.0.0

Context
.
const Tag: <"B">(id: "B") => <Self, Shape>() => Context.TagClass<Self, "B", Shape>

@example

import { Context, Layer } from "effect"
class MyTag extends Context.Tag("MyTag")<
MyTag,
{ readonly myNum: number }
>() {
static Live = Layer.succeed(this, { myNum: 108 })
}

@since2.0.0

Tag
("B")<
class B
B
, { readonly
b: string
b
: string }>() {}
class
class C
C
extends
import Context

@since2.0.0

@since2.0.0

Context
.
const Tag: <"C">(id: "C") => <Self, Shape>() => Context.TagClass<Self, "C", Shape>

@example

import { Context, Layer } from "effect"
class MyTag extends Context.Tag("MyTag")<
MyTag,
{ readonly myNum: number }
>() {
static Live = Layer.succeed(this, { myNum: 108 })
}

@since2.0.0

Tag
("C")<
class C
C
, { readonly
c: boolean
c
: boolean }>() {}
const
const ALive: Layer.Layer<A, never, never>
ALive
=
import Layer
Layer
.
const effect: <typeof A, never, never>(tag: typeof A, effect: Effect.Effect<{
readonly a: number;
}, never, never>) => Layer.Layer<A, never, never> (+1 overload)

Constructs a layer from the specified effect.

@since2.0.0

effect
(
class A
A
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const succeed: <{
a: number;
}>(value: {
a: number;
}) => Effect.Effect<{
a: number;
}, never, never>

Creates an Effect that always succeeds with a given value.

When to Use

Use this function when you need an effect that completes successfully with a specific value without any errors or external dependencies.

@seefail to create an effect that represents a failure.

@example

// Title: Creating a Successful Effect
import { Effect } from "effect"
// Creating an effect that represents a successful scenario
//
// ┌─── Effect<number, never, never>
// ▼
const success = Effect.succeed(42)

@since2.0.0

succeed
({
a: number
a
: 5 }).
Pipeable.pipe<Effect.Effect<{
a: number;
}, never, never>, Effect.Effect<{
a: number;
}, never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<{
a: number;
}, never, never>) => Effect.Effect<{
a: number;
}, never, never>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const tap: <{
a: number;
}, Effect.Effect<void, never, never>>(f: (a: {
a: number;
}) => Effect.Effect<void, never, never>) => <E, R>(self: Effect.Effect<{
a: number;
}, E, R>) => Effect.Effect<...> (+7 overloads)

Runs a side effect with the result of an effect without changing the original value.

When to Use

Use tap when you want to perform a side effect, like logging or tracking, without modifying the main value. This is useful when you need to observe or record an action but want the original value to be passed 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

// Title: Logging a step in a pipeline
import { Console, Effect, pipe } from "effect"
// Function to apply a discount safely to a transaction amount
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: 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

@since2.0.0

tap
(() =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const log: (...message: ReadonlyArray<any>) => Effect.Effect<void, never, never>

Logs one or more messages or error causes at the current log level, which is INFO by default. This function allows logging multiple items at once and can include detailed error information using Cause instances.

To adjust the log level, use the Logger.withMinimumLogLevel function.

@example

import { Cause, Effect } from "effect"
const program = Effect.log(
"message1",
"message2",
Cause.die("Oh no!"),
Cause.die("Oh uh!")
)
// Effect.runFork(program)
// Output:
// timestamp=... level=INFO fiber=#0 message=message1 message=message2 cause="Error: Oh no!
// Error: Oh uh!"

@since2.0.0

log
("initialized"))
)
)
const
const BLive: Layer.Layer<B, never, A>
BLive
=
import Layer
Layer
.
const effect: <typeof B, never, A>(tag: typeof B, effect: Effect.Effect<{
readonly b: string;
}, never, A>) => Layer.Layer<B, never, A> (+1 overload)

Constructs a layer from the specified effect.

@since2.0.0

effect
(
class B
B
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Context.Tag<A, {
readonly a: number;
}>>, {
b: string;
}>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Context.Tag<A, {
readonly a: number;
}>>, {
b: string;
}, never>) => Effect.Effect<...> (+1 overload)

Provides a way to write effectful code using generator functions, simplifying control flow and error handling.

When to Use

gen allows you to write code that looks and behaves like synchronous code, but it can handle asynchronous tasks, errors, and complex control flow (like loops and conditions). It helps make asynchronous code more readable and easier to manage.

The generator functions work similarly to async/await but with more explicit control over the execution of effects. You can yield* values from effects and return the final result at the end.

@example

import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: 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}`
})

@since2.0.0

gen
(function* () {
const {
const a: number
a
} = yield*
class A
A
return {
b: string
b
:
var String: StringConstructor
(value?: any) => string

Allows manipulation and formatting of text strings and determination and location of substrings within strings.

String
(
const a: number
a
) }
})
)
const
const CLive: Layer.Layer<C, never, A>
CLive
=
import Layer
Layer
.
const effect: <typeof C, never, A>(tag: typeof C, effect: Effect.Effect<{
readonly c: boolean;
}, never, A>) => Layer.Layer<C, never, A> (+1 overload)

Constructs a layer from the specified effect.

@since2.0.0

effect
(
class C
C
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Context.Tag<A, {
readonly a: number;
}>>, {
c: boolean;
}>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Context.Tag<A, {
readonly a: number;
}>>, {
c: boolean;
}, never>) => Effect.Effect<...> (+1 overload)

Provides a way to write effectful code using generator functions, simplifying control flow and error handling.

When to Use

gen allows you to write code that looks and behaves like synchronous code, but it can handle asynchronous tasks, errors, and complex control flow (like loops and conditions). It helps make asynchronous code more readable and easier to manage.

The generator functions work similarly to async/await but with more explicit control over the execution of effects. You can yield* values from effects and return the final result at the end.

@example

import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: 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}`
})

@since2.0.0

gen
(function* () {
const {
const a: number
a
} = yield*
class A
A
return {
c: boolean
c
:
const a: number
a
> 0 }
})
)
const
const program: Effect.Effect<void, never, B | C>
program
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Context.Tag<B, {
readonly b: string;
}>> | YieldWrap<Context.Tag<C, {
readonly c: boolean;
}>>, void>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+1 overload)

Provides a way to write effectful code using generator functions, simplifying control flow and error handling.

When to Use

gen allows you to write code that looks and behaves like synchronous code, but it can handle asynchronous tasks, errors, and complex control flow (like loops and conditions). It helps make asynchronous code more readable and easier to manage.

The generator functions work similarly to async/await but with more explicit control over the execution of effects. You can yield* values from effects and return the final result at the end.

@example

import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: 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}`
})

@since2.0.0

gen
(function* () {
yield*
class B
B
yield*
class C
C
})
const
const runnable: Effect.Effect<void, never, never>
runnable
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const provide: <void, never, B | C, B | C, never, never>(self: Effect.Effect<void, never, B | C>, layer: Layer.Layer<B | C, never, never>) => Effect.Effect<void, never, never> (+9 overloads)

Provides the necessary Layers to an effect, removing its dependency on the environment.

You can pass multiple layers, a Context, Runtime, or ManagedRuntime to the effect.

@seeprovideService for providing a service to an effect.

@example

import { Context, Effect, Layer } from "effect"
class Database extends Context.Tag("Database")<
Database,
{ readonly query: (sql: string) => Effect.Effect<Array<unknown>> }
>() {}
const DatabaseLive = Layer.succeed(
Database,
{
// Simulate a database query
query: (sql: string) => Effect.log(`Executing query: ${sql}`).pipe(Effect.as([]))
}
)
// ┌─── Effect<unknown[], never, Database>
// ▼
const program = Effect.gen(function*() {
const database = yield* Database
const result = yield* database.query("SELECT * FROM users")
return result
})
// ┌─── Effect<unknown[], never, never>
// ▼
const runnable = Effect.provide(program, DatabaseLive)
Effect.runPromise(runnable).then(console.log)
// Output:
// timestamp=... level=INFO fiber=#0 message="Executing query: SELECT * FROM users"
// []

@since2.0.0

provide
(
const program: Effect.Effect<void, never, B | C>
program
,
import Layer
Layer
.
const merge: <never, never, B, never, never, C>(self: Layer.Layer<B, never, never>, that: Layer.Layer<C, never, never>) => Layer.Layer<B | C, never, never> (+1 overload)

Merges this layer with the specified layer concurrently, producing a new layer with combined input and output types.

@since2.0.0

merge
(
import Layer
Layer
.
const provide: <A, never, B, never, never, A>(self: Layer.Layer<B, never, A>, that: Layer.Layer<A, never, never>) => Layer.Layer<B, never, never> (+3 overloads)

Feeds the output services of this builder into the input of the specified builder, resulting in a new builder with the inputs of this builder as well as any leftover inputs, and the outputs of the specified builder.

@since2.0.0

provide
(
const BLive: Layer.Layer<B, never, A>
BLive
,
const ALive: Layer.Layer<A, never, never>
ALive
),
import Layer
Layer
.
const provide: <A, never, C, never, never, A>(self: Layer.Layer<C, never, A>, that: Layer.Layer<A, never, never>) => Layer.Layer<C, never, never> (+3 overloads)

Feeds the output services of this builder into the input of the specified builder, resulting in a new builder with the inputs of this builder as well as any leftover inputs, and the outputs of the specified builder.

@since2.0.0

provide
(
const CLive: Layer.Layer<C, never, A>
CLive
,
const ALive: Layer.Layer<A, never, never>
ALive
))
)
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const runPromise: <void, never>(effect: Effect.Effect<void, never, never>, options?: {
readonly signal?: AbortSignal;
} | undefined) => Promise<void>

Executes an effect and returns the result as a Promise.

When to Use

Use runPromise 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.

@seerunPromiseExit for a version that returns an Exit type instead of rejecting.

@example

// Title: Running a Successful Effect as a Promise
import { Effect } from "effect"
Effect.runPromise(Effect.succeed(1)).then(console.log)
// Output: 1

@example

//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

@since2.0.0

runPromise
(
const runnable: Effect.Effect<void, never, never>
runnable
)
/*
Output:
timestamp=... level=INFO fiber=#2 message=initialized
*/

Although both BLive and CLive layers require the ALive layer, the ALive layer is instantiated only once. It is shared with both BLive and CLive.

If we don’t want to share a module, we should create a fresh, non-shared version of it through Layer.fresh.

Example

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Context

@since2.0.0

@since2.0.0

Context
,
import Layer
Layer
} from "effect"
class
class A
A
extends
import Context

@since2.0.0

@since2.0.0

Context
.
const Tag: <"A">(id: "A") => <Self, Shape>() => Context.TagClass<Self, "A", Shape>

@example

import { Context, Layer } from "effect"
class MyTag extends Context.Tag("MyTag")<
MyTag,
{ readonly myNum: number }
>() {
static Live = Layer.succeed(this, { myNum: 108 })
}

@since2.0.0

Tag
("A")<
class A
A
, { readonly
a: number
a
: number }>() {}
class
class B
B
extends
import Context

@since2.0.0

@since2.0.0

Context
.
const Tag: <"B">(id: "B") => <Self, Shape>() => Context.TagClass<Self, "B", Shape>

@example

import { Context, Layer } from "effect"
class MyTag extends Context.Tag("MyTag")<
MyTag,
{ readonly myNum: number }
>() {
static Live = Layer.succeed(this, { myNum: 108 })
}

@since2.0.0

Tag
("B")<
class B
B
, { readonly
b: string
b
: string }>() {}
class
class C
C
extends
import Context

@since2.0.0

@since2.0.0

Context
.
const Tag: <"C">(id: "C") => <Self, Shape>() => Context.TagClass<Self, "C", Shape>

@example

import { Context, Layer } from "effect"
class MyTag extends Context.Tag("MyTag")<
MyTag,
{ readonly myNum: number }
>() {
static Live = Layer.succeed(this, { myNum: 108 })
}

@since2.0.0

Tag
("C")<
class C
C
, { readonly
c: boolean
c
: boolean }>() {}
const
const ALive: Layer.Layer<A, never, never>
ALive
=
import Layer
Layer
.
const effect: <typeof A, never, never>(tag: typeof A, effect: Effect.Effect<{
readonly a: number;
}, never, never>) => Layer.Layer<A, never, never> (+1 overload)

Constructs a layer from the specified effect.

@since2.0.0

effect
(
class A
A
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const succeed: <{
a: number;
}>(value: {
a: number;
}) => Effect.Effect<{
a: number;
}, never, never>

Creates an Effect that always succeeds with a given value.

When to Use

Use this function when you need an effect that completes successfully with a specific value without any errors or external dependencies.

@seefail to create an effect that represents a failure.

@example

// Title: Creating a Successful Effect
import { Effect } from "effect"
// Creating an effect that represents a successful scenario
//
// ┌─── Effect<number, never, never>
// ▼
const success = Effect.succeed(42)

@since2.0.0

succeed
({
a: number
a
: 5 }).
Pipeable.pipe<Effect.Effect<{
a: number;
}, never, never>, Effect.Effect<{
a: number;
}, never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<{
a: number;
}, never, never>) => Effect.Effect<{
a: number;
}, never, never>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const tap: <{
a: number;
}, Effect.Effect<void, never, never>>(f: (a: {
a: number;
}) => Effect.Effect<void, never, never>) => <E, R>(self: Effect.Effect<{
a: number;
}, E, R>) => Effect.Effect<...> (+7 overloads)

Runs a side effect with the result of an effect without changing the original value.

When to Use

Use tap when you want to perform a side effect, like logging or tracking, without modifying the main value. This is useful when you need to observe or record an action but want the original value to be passed 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

// Title: Logging a step in a pipeline
import { Console, Effect, pipe } from "effect"
// Function to apply a discount safely to a transaction amount
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: 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

@since2.0.0

tap
(() =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const log: (...message: ReadonlyArray<any>) => Effect.Effect<void, never, never>

Logs one or more messages or error causes at the current log level, which is INFO by default. This function allows logging multiple items at once and can include detailed error information using Cause instances.

To adjust the log level, use the Logger.withMinimumLogLevel function.

@example

import { Cause, Effect } from "effect"
const program = Effect.log(
"message1",
"message2",
Cause.die("Oh no!"),
Cause.die("Oh uh!")
)
// Effect.runFork(program)
// Output:
// timestamp=... level=INFO fiber=#0 message=message1 message=message2 cause="Error: Oh no!
// Error: Oh uh!"

@since2.0.0

log
("initialized"))
)
)
const
const BLive: Layer.Layer<B, never, A>
BLive
=
import Layer
Layer
.
const effect: <typeof B, never, A>(tag: typeof B, effect: Effect.Effect<{
readonly b: string;
}, never, A>) => Layer.Layer<B, never, A> (+1 overload)

Constructs a layer from the specified effect.

@since2.0.0

effect
(
class B
B
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Context.Tag<A, {
readonly a: number;
}>>, {
b: string;
}>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Context.Tag<A, {
readonly a: number;
}>>, {
b: string;
}, never>) => Effect.Effect<...> (+1 overload)

Provides a way to write effectful code using generator functions, simplifying control flow and error handling.

When to Use

gen allows you to write code that looks and behaves like synchronous code, but it can handle asynchronous tasks, errors, and complex control flow (like loops and conditions). It helps make asynchronous code more readable and easier to manage.

The generator functions work similarly to async/await but with more explicit control over the execution of effects. You can yield* values from effects and return the final result at the end.

@example

import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: 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}`
})

@since2.0.0

gen
(function* () {
const {
const a: number
a
} = yield*
class A
A
return {
b: string
b
:
var String: StringConstructor
(value?: any) => string

Allows manipulation and formatting of text strings and determination and location of substrings within strings.

String
(
const a: number
a
) }
})
)
const
const CLive: Layer.Layer<C, never, A>
CLive
=
import Layer
Layer
.
const effect: <typeof C, never, A>(tag: typeof C, effect: Effect.Effect<{
readonly c: boolean;
}, never, A>) => Layer.Layer<C, never, A> (+1 overload)

Constructs a layer from the specified effect.

@since2.0.0

effect
(
class C
C
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Context.Tag<A, {
readonly a: number;
}>>, {
c: boolean;
}>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Context.Tag<A, {
readonly a: number;
}>>, {
c: boolean;
}, never>) => Effect.Effect<...> (+1 overload)

Provides a way to write effectful code using generator functions, simplifying control flow and error handling.

When to Use

gen allows you to write code that looks and behaves like synchronous code, but it can handle asynchronous tasks, errors, and complex control flow (like loops and conditions). It helps make asynchronous code more readable and easier to manage.

The generator functions work similarly to async/await but with more explicit control over the execution of effects. You can yield* values from effects and return the final result at the end.

@example

import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: 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}`
})

@since2.0.0

gen
(function* () {
const {
const a: number
a
} = yield*
class A
A
return {
c: boolean
c
:
const a: number
a
> 0 }
})
)
const
const program: Effect.Effect<void, never, B | C>
program
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Context.Tag<B, {
readonly b: string;
}>> | YieldWrap<Context.Tag<C, {
readonly c: boolean;
}>>, void>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+1 overload)

Provides a way to write effectful code using generator functions, simplifying control flow and error handling.

When to Use

gen allows you to write code that looks and behaves like synchronous code, but it can handle asynchronous tasks, errors, and complex control flow (like loops and conditions). It helps make asynchronous code more readable and easier to manage.

The generator functions work similarly to async/await but with more explicit control over the execution of effects. You can yield* values from effects and return the final result at the end.

@example

import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: 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}`
})

@since2.0.0

gen
(function* () {
yield*
class B
B
yield*
class C
C
})
const
const runnable: Effect.Effect<void, never, never>
runnable
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const provide: <void, never, B | C, B | C, never, never>(self: Effect.Effect<void, never, B | C>, layer: Layer.Layer<B | C, never, never>) => Effect.Effect<void, never, never> (+9 overloads)

Provides the necessary Layers to an effect, removing its dependency on the environment.

You can pass multiple layers, a Context, Runtime, or ManagedRuntime to the effect.

@seeprovideService for providing a service to an effect.

@example

import { Context, Effect, Layer } from "effect"
class Database extends Context.Tag("Database")<
Database,
{ readonly query: (sql: string) => Effect.Effect<Array<unknown>> }
>() {}
const DatabaseLive = Layer.succeed(
Database,
{
// Simulate a database query
query: (sql: string) => Effect.log(`Executing query: ${sql}`).pipe(Effect.as([]))
}
)
// ┌─── Effect<unknown[], never, Database>
// ▼
const program = Effect.gen(function*() {
const database = yield* Database
const result = yield* database.query("SELECT * FROM users")
return result
})
// ┌─── Effect<unknown[], never, never>
// ▼
const runnable = Effect.provide(program, DatabaseLive)
Effect.runPromise(runnable).then(console.log)
// Output:
// timestamp=... level=INFO fiber=#0 message="Executing query: SELECT * FROM users"
// []

@since2.0.0

provide
(
const program: Effect.Effect<void, never, B | C>
program
,
import Layer
Layer
.
const merge: <never, never, B, never, never, C>(self: Layer.Layer<B, never, never>, that: Layer.Layer<C, never, never>) => Layer.Layer<B | C, never, never> (+1 overload)

Merges this layer with the specified layer concurrently, producing a new layer with combined input and output types.

@since2.0.0

merge
(
import Layer
Layer
.
const provide: <A, never, B, never, never, A>(self: Layer.Layer<B, never, A>, that: Layer.Layer<A, never, never>) => Layer.Layer<B, never, never> (+3 overloads)

Feeds the output services of this builder into the input of the specified builder, resulting in a new builder with the inputs of this builder as well as any leftover inputs, and the outputs of the specified builder.

@since2.0.0

provide
(
const BLive: Layer.Layer<B, never, A>
BLive
,
import Layer
Layer
.
const fresh: <A, never, never>(self: Layer.Layer<A, never, never>) => Layer.Layer<A, never, never>

Creates a fresh version of this layer that will not be shared.

@since2.0.0

fresh
(
const ALive: Layer.Layer<A, never, never>
ALive
)),
import Layer
Layer
.
const provide: <A, never, C, never, never, A>(self: Layer.Layer<C, never, A>, that: Layer.Layer<A, never, never>) => Layer.Layer<C, never, never> (+3 overloads)

Feeds the output services of this builder into the input of the specified builder, resulting in a new builder with the inputs of this builder as well as any leftover inputs, and the outputs of the specified builder.

@since2.0.0

provide
(
const CLive: Layer.Layer<C, never, A>
CLive
,
import Layer
Layer
.
const fresh: <A, never, never>(self: Layer.Layer<A, never, never>) => Layer.Layer<A, never, never>

Creates a fresh version of this layer that will not be shared.

@since2.0.0

fresh
(
const ALive: Layer.Layer<A, never, never>
ALive
))
)
)
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const runPromise: <void, never>(effect: Effect.Effect<void, never, never>, options?: {
readonly signal?: AbortSignal;
} | undefined) => Promise<void>

Executes an effect and returns the result as a Promise.

When to Use

Use runPromise 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.

@seerunPromiseExit for a version that returns an Exit type instead of rejecting.

@example

// Title: Running a Successful Effect as a Promise
import { Effect } from "effect"
Effect.runPromise(Effect.succeed(1)).then(console.log)
// Output: 1

@example

//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

@since2.0.0

runPromise
(
const runnable: Effect.Effect<void, never, never>
runnable
)
/*
Output:
timestamp=... level=INFO fiber=#2 message=initialized
timestamp=... level=INFO fiber=#3 message=initialized
*/

If we don’t provide a layer globally but instead provide them locally, that layer doesn’t support memoization by default.

Example

In the following example, we provided the ALive layer two times locally, and Effect doesn’t memoize the construction of the ALive layer. So, it will be initialized two times:

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Context

@since2.0.0

@since2.0.0

Context
,
import Layer
Layer
} from "effect"
class
class A
A
extends
import Context

@since2.0.0

@since2.0.0

Context
.
const Tag: <"A">(id: "A") => <Self, Shape>() => Context.TagClass<Self, "A", Shape>

@example

import { Context, Layer } from "effect"
class MyTag extends Context.Tag("MyTag")<
MyTag,
{ readonly myNum: number }
>() {
static Live = Layer.succeed(this, { myNum: 108 })
}

@since2.0.0

Tag
("A")<
class A
A
, { readonly
a: number
a
: number }>() {}
const
const Alive: Layer.Layer<A, never, never>
Alive
=
import Layer
Layer
.
const effect: <typeof A, never, never>(tag: typeof A, effect: Effect.Effect<{
readonly a: number;
}, never, never>) => Layer.Layer<A, never, never> (+1 overload)

Constructs a layer from the specified effect.

@since2.0.0

effect
(
class A
A
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const succeed: <{
a: number;
}>(value: {
a: number;
}) => Effect.Effect<{
a: number;
}, never, never>

Creates an Effect that always succeeds with a given value.

When to Use

Use this function when you need an effect that completes successfully with a specific value without any errors or external dependencies.

@seefail to create an effect that represents a failure.

@example

// Title: Creating a Successful Effect
import { Effect } from "effect"
// Creating an effect that represents a successful scenario
//
// ┌─── Effect<number, never, never>
// ▼
const success = Effect.succeed(42)

@since2.0.0

succeed
({
a: number
a
: 5 }).
Pipeable.pipe<Effect.Effect<{
a: number;
}, never, never>, Effect.Effect<{
a: number;
}, never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<{
a: number;
}, never, never>) => Effect.Effect<{
a: number;
}, never, never>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const tap: <{
a: number;
}, Effect.Effect<void, never, never>>(f: (a: {
a: number;
}) => Effect.Effect<void, never, never>) => <E, R>(self: Effect.Effect<{
a: number;
}, E, R>) => Effect.Effect<...> (+7 overloads)

Runs a side effect with the result of an effect without changing the original value.

When to Use

Use tap when you want to perform a side effect, like logging or tracking, without modifying the main value. This is useful when you need to observe or record an action but want the original value to be passed 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

// Title: Logging a step in a pipeline
import { Console, Effect, pipe } from "effect"
// Function to apply a discount safely to a transaction amount
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: 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

@since2.0.0

tap
(() =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const log: (...message: ReadonlyArray<any>) => Effect.Effect<void, never, never>

Logs one or more messages or error causes at the current log level, which is INFO by default. This function allows logging multiple items at once and can include detailed error information using Cause instances.

To adjust the log level, use the Logger.withMinimumLogLevel function.

@example

import { Cause, Effect } from "effect"
const program = Effect.log(
"message1",
"message2",
Cause.die("Oh no!"),
Cause.die("Oh uh!")
)
// Effect.runFork(program)
// Output:
// timestamp=... level=INFO fiber=#0 message=message1 message=message2 cause="Error: Oh no!
// Error: Oh uh!"

@since2.0.0

log
("initialized"))
)
)
const
const program: Effect.Effect<void, never, never>
program
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<{
readonly a: number;
}, never, never>>, void>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<{
readonly a: number;
}, never, never>>, void, never>) => Effect.Effect<...> (+1 overload)

Provides a way to write effectful code using generator functions, simplifying control flow and error handling.

When to Use

gen allows you to write code that looks and behaves like synchronous code, but it can handle asynchronous tasks, errors, and complex control flow (like loops and conditions). It helps make asynchronous code more readable and easier to manage.

The generator functions work similarly to async/await but with more explicit control over the execution of effects. You can yield* values from effects and return the final result at the end.

@example

import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: 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}`
})

@since2.0.0

gen
(function* () {
yield*
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const provide: <{
readonly a: number;
}, never, A, A, never, never>(self: Effect.Effect<{
readonly a: number;
}, never, A>, layer: Layer.Layer<A, never, never>) => Effect.Effect<{
readonly a: number;
}, never, never> (+9 overloads)

Provides the necessary Layers to an effect, removing its dependency on the environment.

You can pass multiple layers, a Context, Runtime, or ManagedRuntime to the effect.

@seeprovideService for providing a service to an effect.

@example

import { Context, Effect, Layer } from "effect"
class Database extends Context.Tag("Database")<
Database,
{ readonly query: (sql: string) => Effect.Effect<Array<unknown>> }
>() {}
const DatabaseLive = Layer.succeed(
Database,
{
// Simulate a database query
query: (sql: string) => Effect.log(`Executing query: ${sql}`).pipe(Effect.as([]))
}
)
// ┌─── Effect<unknown[], never, Database>
// ▼
const program = Effect.gen(function*() {
const database = yield* Database
const result = yield* database.query("SELECT * FROM users")
return result
})
// ┌─── Effect<unknown[], never, never>
// ▼
const runnable = Effect.provide(program, DatabaseLive)
Effect.runPromise(runnable).then(console.log)
// Output:
// timestamp=... level=INFO fiber=#0 message="Executing query: SELECT * FROM users"
// []

@since2.0.0

provide
(
class A
A
,
const Alive: Layer.Layer<A, never, never>
Alive
)
yield*
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const provide: <{
readonly a: number;
}, never, A, A, never, never>(self: Effect.Effect<{
readonly a: number;
}, never, A>, layer: Layer.Layer<A, never, never>) => Effect.Effect<{
readonly a: number;
}, never, never> (+9 overloads)

Provides the necessary Layers to an effect, removing its dependency on the environment.

You can pass multiple layers, a Context, Runtime, or ManagedRuntime to the effect.

@seeprovideService for providing a service to an effect.

@example

import { Context, Effect, Layer } from "effect"
class Database extends Context.Tag("Database")<
Database,
{ readonly query: (sql: string) => Effect.Effect<Array<unknown>> }
>() {}
const DatabaseLive = Layer.succeed(
Database,
{
// Simulate a database query
query: (sql: string) => Effect.log(`Executing query: ${sql}`).pipe(Effect.as([]))
}
)
// ┌─── Effect<unknown[], never, Database>
// ▼
const program = Effect.gen(function*() {
const database = yield* Database
const result = yield* database.query("SELECT * FROM users")
return result
})
// ┌─── Effect<unknown[], never, never>
// ▼
const runnable = Effect.provide(program, DatabaseLive)
Effect.runPromise(runnable).then(console.log)
// Output:
// timestamp=... level=INFO fiber=#0 message="Executing query: SELECT * FROM users"
// []

@since2.0.0

provide
(
class A
A
,
const Alive: Layer.Layer<A, never, never>
Alive
)
})
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const runPromise: <void, never>(effect: Effect.Effect<void, never, never>, options?: {
readonly signal?: AbortSignal;
} | undefined) => Promise<void>

Executes an effect and returns the result as a Promise.

When to Use

Use runPromise 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.

@seerunPromiseExit for a version that returns an Exit type instead of rejecting.

@example

// Title: Running a Successful Effect as a Promise
import { Effect } from "effect"
Effect.runPromise(Effect.succeed(1)).then(console.log)
// Output: 1

@example

//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

@since2.0.0

runPromise
(
const program: Effect.Effect<void, never, never>
program
)
/*
Output:
timestamp=... level=INFO fiber=#0 message=initialized
timestamp=... level=INFO fiber=#0 message=initialized
*/

We can memoize a layer manually using the Layer.memoize function. It will return a scoped effect that, if evaluated, will return the lazily computed result of this layer.

Example

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Context

@since2.0.0

@since2.0.0

Context
,
import Layer
Layer
} from "effect"
class
class A
A
extends
import Context

@since2.0.0

@since2.0.0

Context
.
const Tag: <"A">(id: "A") => <Self, Shape>() => Context.TagClass<Self, "A", Shape>

@example

import { Context, Layer } from "effect"
class MyTag extends Context.Tag("MyTag")<
MyTag,
{ readonly myNum: number }
>() {
static Live = Layer.succeed(this, { myNum: 108 })
}

@since2.0.0

Tag
("A")<
class A
A
, { readonly
a: number
a
: number }>() {}
const
const ALive: Layer.Layer<A, never, never>
ALive
=
import Layer
Layer
.
const effect: <typeof A, never, never>(tag: typeof A, effect: Effect.Effect<{
readonly a: number;
}, never, never>) => Layer.Layer<A, never, never> (+1 overload)

Constructs a layer from the specified effect.

@since2.0.0

effect
(
class A
A
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const succeed: <{
a: number;
}>(value: {
a: number;
}) => Effect.Effect<{
a: number;
}, never, never>

Creates an Effect that always succeeds with a given value.

When to Use

Use this function when you need an effect that completes successfully with a specific value without any errors or external dependencies.

@seefail to create an effect that represents a failure.

@example

// Title: Creating a Successful Effect
import { Effect } from "effect"
// Creating an effect that represents a successful scenario
//
// ┌─── Effect<number, never, never>
// ▼
const success = Effect.succeed(42)

@since2.0.0

succeed
({
a: number
a
: 5 }).
Pipeable.pipe<Effect.Effect<{
a: number;
}, never, never>, Effect.Effect<{
a: number;
}, never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<{
a: number;
}, never, never>) => Effect.Effect<{
a: number;
}, never, never>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const tap: <{
a: number;
}, Effect.Effect<void, never, never>>(f: (a: {
a: number;
}) => Effect.Effect<void, never, never>) => <E, R>(self: Effect.Effect<{
a: number;
}, E, R>) => Effect.Effect<...> (+7 overloads)

Runs a side effect with the result of an effect without changing the original value.

When to Use

Use tap when you want to perform a side effect, like logging or tracking, without modifying the main value. This is useful when you need to observe or record an action but want the original value to be passed 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

// Title: Logging a step in a pipeline
import { Console, Effect, pipe } from "effect"
// Function to apply a discount safely to a transaction amount
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: 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

@since2.0.0

tap
(() =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const log: (...message: ReadonlyArray<any>) => Effect.Effect<void, never, never>

Logs one or more messages or error causes at the current log level, which is INFO by default. This function allows logging multiple items at once and can include detailed error information using Cause instances.

To adjust the log level, use the Logger.withMinimumLogLevel function.

@example

import { Cause, Effect } from "effect"
const program = Effect.log(
"message1",
"message2",
Cause.die("Oh no!"),
Cause.die("Oh uh!")
)
// Effect.runFork(program)
// Output:
// timestamp=... level=INFO fiber=#0 message=message1 message=message2 cause="Error: Oh no!
// Error: Oh uh!"

@since2.0.0

log
("initialized"))
)
)
const
const program: Effect.Effect<void, never, never>
program
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const scoped: <void, never, Scope>(effect: Effect.Effect<void, never, Scope>) => Effect.Effect<void, never, never>

Scopes all resources used in this workflow to the lifetime of the workflow, ensuring that their finalizers are run as soon as this workflow completes execution, whether by success, failure, or interruption.

@since2.0.0

scoped
(
import Layer
Layer
.
const memoize: <never, never, A>(self: Layer.Layer<A, never, never>) => Effect.Effect<Layer.Layer<A, never, never>, never, Scope>

Returns a scoped effect that, if evaluated, will return the lazily computed result of this layer.

@since2.0.0

memoize
(
const ALive: Layer.Layer<A, never, never>
ALive
).
Pipeable.pipe<Effect.Effect<Layer.Layer<A, never, never>, never, Scope>, Effect.Effect<void, never, Scope>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<Layer.Layer<A, never, never>, never, Scope>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const andThen: <Layer.Layer<A, never, never>, Effect.Effect<void, never, never>>(f: (a: Layer.Layer<A, never, never>) => Effect.Effect<void, never, never>) => <E, R>(self: Effect.Effect<...>) => Effect.Effect<...> (+3 overloads)

Chains two actions, where the second action can depend on the result of the first.

Syntax

const transformedEffect = pipe(myEffect, Effect.andThen(anotherEffect))
// or
const transformedEffect = Effect.andThen(myEffect, anotherEffect)
// or
const transformedEffect = myEffect.pipe(Effect.andThen(anotherEffect))

When to Use

Use andThen when you need to run multiple actions in sequence, with the second action depending on the result of the first. This is useful for combining effects or handling computations that must happen in order.

Details

The second action can be:

  • A constant value (similar to

as

)

  • A function returning a value (similar to

map

)

  • A Promise
  • A function returning a Promise
  • An Effect
  • A function returning an Effect (similar to

flatMap

)

Note: andThen works well with both Option and Either types, treating them as effects.

@example

// Title: Applying a Discount Based on Fetched Amount
import { pipe, Effect } from "effect"
// Function to apply a discount safely to a transaction amount
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: 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) => amount * 2),
Effect.andThen((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result2).then(console.log)
// Output: 190

@since2.0.0

andThen
((
memoized: Layer.Layer<A, never, never>
memoized
) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<{
readonly a: number;
}, never, never>>, void>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<{
readonly a: number;
}, never, never>>, void, never>) => Effect.Effect<...> (+1 overload)

Provides a way to write effectful code using generator functions, simplifying control flow and error handling.

When to Use

gen allows you to write code that looks and behaves like synchronous code, but it can handle asynchronous tasks, errors, and complex control flow (like loops and conditions). It helps make asynchronous code more readable and easier to manage.

The generator functions work similarly to async/await but with more explicit control over the execution of effects. You can yield* values from effects and return the final result at the end.

@example

import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: 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}`
})

@since2.0.0

gen
(function* () {
yield*
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const provide: <{
readonly a: number;
}, never, A, A, never, never>(self: Effect.Effect<{
readonly a: number;
}, never, A>, layer: Layer.Layer<A, never, never>) => Effect.Effect<{
readonly a: number;
}, never, never> (+9 overloads)

Provides the necessary Layers to an effect, removing its dependency on the environment.

You can pass multiple layers, a Context, Runtime, or ManagedRuntime to the effect.

@seeprovideService for providing a service to an effect.

@example

import { Context, Effect, Layer } from "effect"
class Database extends Context.Tag("Database")<
Database,
{ readonly query: (sql: string) => Effect.Effect<Array<unknown>> }
>() {}
const DatabaseLive = Layer.succeed(
Database,
{
// Simulate a database query
query: (sql: string) => Effect.log(`Executing query: ${sql}`).pipe(Effect.as([]))
}
)
// ┌─── Effect<unknown[], never, Database>
// ▼
const program = Effect.gen(function*() {
const database = yield* Database
const result = yield* database.query("SELECT * FROM users")
return result
})
// ┌─── Effect<unknown[], never, never>
// ▼
const runnable = Effect.provide(program, DatabaseLive)
Effect.runPromise(runnable).then(console.log)
// Output:
// timestamp=... level=INFO fiber=#0 message="Executing query: SELECT * FROM users"
// []

@since2.0.0

provide
(
class A
A
,
memoized: Layer.Layer<A, never, never>
memoized
)
yield*
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const provide: <{
readonly a: number;
}, never, A, A, never, never>(self: Effect.Effect<{
readonly a: number;
}, never, A>, layer: Layer.Layer<A, never, never>) => Effect.Effect<{
readonly a: number;
}, never, never> (+9 overloads)

Provides the necessary Layers to an effect, removing its dependency on the environment.

You can pass multiple layers, a Context, Runtime, or ManagedRuntime to the effect.

@seeprovideService for providing a service to an effect.

@example

import { Context, Effect, Layer } from "effect"
class Database extends Context.Tag("Database")<
Database,
{ readonly query: (sql: string) => Effect.Effect<Array<unknown>> }
>() {}
const DatabaseLive = Layer.succeed(
Database,
{
// Simulate a database query
query: (sql: string) => Effect.log(`Executing query: ${sql}`).pipe(Effect.as([]))
}
)
// ┌─── Effect<unknown[], never, Database>
// ▼
const program = Effect.gen(function*() {
const database = yield* Database
const result = yield* database.query("SELECT * FROM users")
return result
})
// ┌─── Effect<unknown[], never, never>
// ▼
const runnable = Effect.provide(program, DatabaseLive)
Effect.runPromise(runnable).then(console.log)
// Output:
// timestamp=... level=INFO fiber=#0 message="Executing query: SELECT * FROM users"
// []

@since2.0.0

provide
(
class A
A
,
memoized: Layer.Layer<A, never, never>
memoized
)
})
)
)
)
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const runPromise: <void, never>(effect: Effect.Effect<void, never, never>, options?: {
readonly signal?: AbortSignal;
} | undefined) => Promise<void>

Executes an effect and returns the result as a Promise.

When to Use

Use runPromise 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.

@seerunPromiseExit for a version that returns an Exit type instead of rejecting.

@example

// Title: Running a Successful Effect as a Promise
import { Effect } from "effect"
Effect.runPromise(Effect.succeed(1)).then(console.log)
// Output: 1

@example

//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

@since2.0.0

runPromise
(
const program: Effect.Effect<void, never, never>
program
)
/*
Output:
timestamp=... level=INFO fiber=#0 message=initialized
*/