Skip to content

Introduction to Runtime

The Runtime<R> data type represents a runtime system that can execute effects. To run an effect, Effect<A, E, R>, we need a Runtime<R> that contains the required resources, denoted by the R type parameter.

A Runtime<R> consists of three main components:

  • A value of type Context<R>
  • A value of type FiberRefs
  • A value of type RuntimeFlags

When we write an Effect program, we construct an Effect using constructors and combinators. Essentially, we are creating a blueprint of a program. An Effect is merely a data structure that describes the execution of a concurrent program. It represents a tree-like structure that combines various primitives to define what the effect should do.

However, this data structure itself does not perform any actions, it is solely a description of a concurrent program.

To execute this program, the Effect runtime system comes into play. The Runtime.run* functions (e.g., Runtime.runPromise, Runtime.runFork) are responsible for taking this blueprint and executing it.

When the runtime system runs an effect, it creates a root fiber, initializing it with:

  • The initial context
  • The initial FiberRefs
  • The initial effect

It then starts a loop, executing the instructions described by the Effect step by step.

You can think of the runtime as a system that takes an Effect<A, E, R> and its associated context Context<R> and produces an Exit<A, E> result.

┌────────────────────────────────┐
│ Context<R> + Effect<A, E, R> │
└────────────────────────────────┘
┌────────────────────────────────┐
│ Effect Runtime System │
└────────────────────────────────┘
┌────────────────────────────────┐
│ Exit<A, E> │
└────────────────────────────────┘

Runtime Systems have a lot of responsibilities:

ResponsibilityDescription
Executing the programThe runtime must execute every step of the effect in a loop until the program completes.
Handling errorsIt handles both expected and unexpected errors that occur during execution.
Managing concurrencyThe runtime spawns new fibers when Effect.fork is called to handle concurrent operations.
Cooperative yieldingIt ensures fibers don’t monopolize resources, yielding control when necessary.
Ensuring resource cleanupThe runtime guarantees finalizers run properly to clean up resources when needed.
Handling async callbacksThe runtime deals with asynchronous operations transparently, allowing you to write async and sync code uniformly.

When we use functions that run effects like Effect.runPromise or Effect.runFork, we are actually using the default runtime without explicitly mentioning it. These functions are designed as convenient shortcuts for executing our effects using the default runtime.

Each of the Effect.run* functions internally calls the corresponding Runtime.run* function, passing in the default runtime. For example, Effect.runPromise is just an alias for Runtime.runPromise(defaultRuntime).

Both of the following executions are functionally equivalent:

Example (Running an Effect Using the Default Runtime)

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Runtime
Runtime
} from "effect"
const
const program: Effect.Effect<void, never, never>
program
=
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
("Application started!")
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="Application started!"
*/
import Runtime
Runtime
.
const runPromise: <never>(runtime: Runtime.Runtime<never>) => <A, E>(effect: Effect.Effect<A, E, never>, options?: {
readonly signal?: AbortSignal;
} | undefined) => Promise<...>

Runs the Effect, returning a JavaScript Promise that will be resolved with the value of the effect once the effect has been executed, or will be rejected with the first error or exception throw by the effect.

This method is effectful and should only be used at the edges of your program.

@since2.0.0

runPromise
(
import Runtime
Runtime
.
const defaultRuntime: Runtime.Runtime<never>

@since2.0.0

defaultRuntime
)(
const program: Effect.Effect<void, never, never>
program
)
/*
Output:
timestamp=... level=INFO fiber=#0 message="Application started!"
*/

In both cases, the program runs using the default runtime, producing the same output.

The default runtime includes:

  • An empty context
  • A set of FiberRefs that include the default services
  • A default configuration for RuntimeFlags that enables Interruption and CooperativeYielding

In most scenarios, using the default runtime is sufficient for effect execution. However, there are cases where it’s helpful to create a custom runtime, particularly when you need to reuse specific configurations or contexts.

For example, in a React app or when executing operations on a server in response to API requests, you might create a Runtime<R> by initializing a layer Layer<R, Err, RIn>. This allows you to maintain a consistent context across different execution boundaries.

In Effect, runtime configurations are typically inherited from their parent workflows. This means that when we access a runtime configuration or obtain a runtime inside a workflow, we are essentially using the configuration of the parent workflow.

However, there are cases where we want to temporarily override the runtime configuration for a specific part of our code. This concept is known as locally scoped runtime configuration. Once the execution of that code region is completed, the runtime configuration reverts to its original settings.

To achieve this, we make use of the Effect.provide function, which allow us to provide a new runtime configuration to a specific section of our code.

Example (Overriding the Logger Configuration)

In this example, we create a simple logger using Logger.replace, which replaces the default logger with a custom one that logs messages without timestamps or levels. We then use Effect.provide to apply this custom logger to the program.

import {
import Logger
Logger
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
} from "effect"
const
const addSimpleLogger: Layer<never, never, never>
addSimpleLogger
=
import Logger
Logger
.
const replace: <void, void>(self: Logger.Logger<unknown, void>, that: Logger.Logger<unknown, void>) => Layer<never> (+1 overload)

@since2.0.0

replace
(
import Logger
Logger
.
const defaultLogger: Logger.Logger<unknown, void>

@since2.0.0

defaultLogger
,
// Custom logger implementation
import Logger
Logger
.
const make: <unknown, void>(log: (options: Logger.Logger<in Message, out Output>.Options<unknown>) => void) => Logger.Logger<unknown, void>

Creates a custom logger that formats log messages according to the provided function.

@example

import { Effect, Logger, LogLevel } from "effect"
const logger = Logger.make(({ logLevel, message }) => {
globalThis.console.log(`[${logLevel.label}] ${message}`)
})
const task1 = Effect.logDebug("task1 done")
const task2 = Effect.logDebug("task2 done")
const program = Effect.gen(function*() {
yield* Effect.log("start")
yield* task1
yield* task2
yield* Effect.log("done")
}).pipe(
Logger.withMinimumLogLevel(LogLevel.Debug),
Effect.provide(Logger.replace(Logger.defaultLogger, logger))
)
// Effect.runFork(program)
// [INFO] start
// [DEBUG] task1 done
// [DEBUG] task2 done
// [INFO] done

@since2.0.0

make
(({
message: unknown
message
}) =>
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err

@seesource

console
.
Console.log(message?: any, ...optionalParams: any[]): void

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout

See util.format() for more information.

@sincev0.1.100

log
(
message: unknown
message
))
)
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<void, never, never>>, void>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<void, 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 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
("Application started!")
yield*
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
("Application is about to exit!")
})
// Running with the default logger
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const runFork: <void, never>(effect: Effect.Effect<void, never, never>, options?: RunForkOptions) => RuntimeFiber<void, never>

The foundational function for running effects, returning a "fiber" that can be observed or interrupted.

When to Use

runFork is used to run an effect in the background by creating a fiber. It is the base function for all other run functions. It starts a fiber that can be observed or interrupted.

Unless you specifically need a Promise or synchronous operation, runFork is a good default choice.

@example

// Title: Running an Effect in the Background
import { Effect, Console, Schedule, Fiber } from "effect"
// ┌─── Effect<number, never, never>
// ▼
const program = Effect.repeat(
Console.log("running..."),
Schedule.spaced("200 millis")
)
// ┌─── RuntimeFiber<number, never>
// ▼
const fiber = Effect.runFork(program)
setTimeout(() => {
Effect.runFork(Fiber.interrupt(fiber))
}, 500)

@since2.0.0

runFork
(
const program: Effect.Effect<void, never, never>
program
)
/*
Output:
timestamp=... level=INFO fiber=#0 message="Application started!"
timestamp=... level=INFO fiber=#0 message="Application is about to exit!"
*/
// Overriding the default logger with a custom one
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const runFork: <void, never>(effect: Effect.Effect<void, never, never>, options?: RunForkOptions) => RuntimeFiber<void, never>

The foundational function for running effects, returning a "fiber" that can be observed or interrupted.

When to Use

runFork is used to run an effect in the background by creating a fiber. It is the base function for all other run functions. It starts a fiber that can be observed or interrupted.

Unless you specifically need a Promise or synchronous operation, runFork is a good default choice.

@example

// Title: Running an Effect in the Background
import { Effect, Console, Schedule, Fiber } from "effect"
// ┌─── Effect<number, never, never>
// ▼
const program = Effect.repeat(
Console.log("running..."),
Schedule.spaced("200 millis")
)
// ┌─── RuntimeFiber<number, never>
// ▼
const fiber = Effect.runFork(program)
setTimeout(() => {
Effect.runFork(Fiber.interrupt(fiber))
}, 500)

@since2.0.0

runFork
(
const program: Effect.Effect<void, never, never>
program
.
Pipeable.pipe<Effect.Effect<void, never, never>, Effect.Effect<void, never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<void, never, never>) => Effect.Effect<void, never, never>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const provide: <never, never, never>(layer: Layer<never, never, never>) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, 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 addSimpleLogger: Layer<never, never, never>
addSimpleLogger
)))
/*
Output:
[ 'Application started!' ]
[ 'Application is about to exit!' ]
*/

To ensure that the runtime configuration is only applied to a specific part of an Effect application, we should provide the configuration layer exclusively to that particular section.

Example (Providing a configuration layer to a nested workflow)

In this example, we demonstrate how to apply a custom logger configuration only to a specific section of the program. The default logger is used for most of the program, but when we apply the Effect.provide(addSimpleLogger) call, it overrides the logger within that specific nested block. After that, the configuration reverts to its original state.

import {
import Logger
Logger
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
} from "effect"
const
const addSimpleLogger: Layer<never, never, never>
addSimpleLogger
=
import Logger
Logger
.
const replace: <void, void>(self: Logger.Logger<unknown, void>, that: Logger.Logger<unknown, void>) => Layer<never> (+1 overload)

@since2.0.0

replace
(
import Logger
Logger
.
const defaultLogger: Logger.Logger<unknown, void>

@since2.0.0

defaultLogger
,
// Custom logger implementation
import Logger
Logger
.
const make: <unknown, void>(log: (options: Logger.Logger<in Message, out Output>.Options<unknown>) => void) => Logger.Logger<unknown, void>

Creates a custom logger that formats log messages according to the provided function.

@example

import { Effect, Logger, LogLevel } from "effect"
const logger = Logger.make(({ logLevel, message }) => {
globalThis.console.log(`[${logLevel.label}] ${message}`)
})
const task1 = Effect.logDebug("task1 done")
const task2 = Effect.logDebug("task2 done")
const program = Effect.gen(function*() {
yield* Effect.log("start")
yield* task1
yield* task2
yield* Effect.log("done")
}).pipe(
Logger.withMinimumLogLevel(LogLevel.Debug),
Effect.provide(Logger.replace(Logger.defaultLogger, logger))
)
// Effect.runFork(program)
// [INFO] start
// [DEBUG] task1 done
// [DEBUG] task2 done
// [INFO] done

@since2.0.0

make
(({
message: unknown
message
}) =>
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err

@seesource

console
.
Console.log(message?: any, ...optionalParams: any[]): void

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout

See util.format() for more information.

@sincev0.1.100

log
(
message: unknown
message
))
)
const
const removeDefaultLogger: Layer<never, never, never>
removeDefaultLogger
=
import Logger
Logger
.
const remove: <void>(logger: Logger.Logger<unknown, void>) => Layer<never>

@since2.0.0

remove
(
import Logger
Logger
.
const defaultLogger: Logger.Logger<unknown, void>

@since2.0.0

defaultLogger
)
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<void, never, never>>, void>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<void, 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* () {
// Logs with default logger
yield*
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
("Application started!")
yield*
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<void, never, never>>, void>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<void, 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* () {
// This log is suppressed
yield*
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
("I'm not going to be logged!")
// Custom logger applied here
yield*
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
("I will be logged by the simple logger.").
Pipeable.pipe<Effect.Effect<void, never, never>, Effect.Effect<void, never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<void, never, never>) => Effect.Effect<void, never, never>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const provide: <never, never, never>(layer: Layer<never, never, never>) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, 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 addSimpleLogger: Layer<never, never, never>
addSimpleLogger
)
)
// This log is suppressed
yield*
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
(
"Reset back to the previous configuration, so I won't be logged."
)
}).
Pipeable.pipe<Effect.Effect<void, never, never>, Effect.Effect<void, never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<void, never, never>) => Effect.Effect<void, never, never>): Effect.Effect<...> (+21 overloads)
pipe
(
// Remove the default logger temporarily
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const provide: <never, never, never>(layer: Layer<never, never, never>) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, 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 removeDefaultLogger: Layer<never, never, never>
removeDefaultLogger
)
)
// Logs with default logger again
yield*
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
("Application is about to exit!")
})
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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

Executes an effect synchronously, running it immediately and returning the result.

When to Use

Use runSync to run an effect that does not fail and does not include any asynchronous operations.

If the effect fails or involves asynchronous work, it will throw an error, and execution will stop where the failure or async operation occurs.

@seerunSyncExit for a version that returns an Exit type instead of throwing an error.

@example

// Title: Synchronous Logging
import { Effect } from "effect"
const program = Effect.sync(() => {
console.log("Hello, World!")
return 1
})
const result = Effect.runSync(program)
// Output: Hello, World!
console.log(result)
// Output: 1

@example

// Title: Incorrect Usage with Failing or Async Effects import { Effect } from "effect"

try { // Attempt to run an effect that fails Effect.runSync(Effect.fail("my error")) } catch (e) { console.error(e) } // Output: // (FiberFailure) Error: my error

try { // Attempt to run an effect that involves async work Effect.runSync(Effect.promise(() => Promise.resolve(1))) } catch (e) { console.error(e) } // Output: // (FiberFailure) AsyncFiberException: Fiber #0 cannot be resolved synchronously. This is caused by using runSync on an effect that performs async work

@since2.0.0

runSync
(
const program: Effect.Effect<void, never, never>
program
)
/*
Output:
timestamp=... level=INFO fiber=#0 message="Application started!"
[ 'I will be logged by the simple logger.' ]
timestamp=... level=INFO fiber=#0 message="Application is about to exit!"
*/

When developing an Effect application and using Effect.run* functions to execute it, the application is automatically run using the default runtime behind the scenes. While it’s possible to adjust specific parts of the application by providing locally scoped configuration layers using Effect.provide, there are scenarios where you might want to customize the runtime configuration for the entire application from the top level.

In these cases, you can create a top-level runtime by converting a configuration layer into a runtime using the ManagedRuntime.make constructor.

Example (Creating and Using a Custom Managed Runtime)

In this example, we first create a custom configuration layer called appLayer, which replaces the default logger with a simple one that logs messages to the console. Next, we use ManagedRuntime.make to turn this configuration layer into a runtime.

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import ManagedRuntime
ManagedRuntime
,
import Logger
Logger
} from "effect"
// Define a configuration layer that replaces the default logger
const
const appLayer: Layer<never, never, never>
appLayer
=
import Logger
Logger
.
const replace: <void, void>(self: Logger.Logger<unknown, void>, that: Logger.Logger<unknown, void>) => Layer<never> (+1 overload)

@since2.0.0

replace
(
import Logger
Logger
.
const defaultLogger: Logger.Logger<unknown, void>

@since2.0.0

defaultLogger
,
// Custom logger implementation
import Logger
Logger
.
const make: <unknown, void>(log: (options: Logger.Logger<in Message, out Output>.Options<unknown>) => void) => Logger.Logger<unknown, void>

Creates a custom logger that formats log messages according to the provided function.

@example

import { Effect, Logger, LogLevel } from "effect"
const logger = Logger.make(({ logLevel, message }) => {
globalThis.console.log(`[${logLevel.label}] ${message}`)
})
const task1 = Effect.logDebug("task1 done")
const task2 = Effect.logDebug("task2 done")
const program = Effect.gen(function*() {
yield* Effect.log("start")
yield* task1
yield* task2
yield* Effect.log("done")
}).pipe(
Logger.withMinimumLogLevel(LogLevel.Debug),
Effect.provide(Logger.replace(Logger.defaultLogger, logger))
)
// Effect.runFork(program)
// [INFO] start
// [DEBUG] task1 done
// [DEBUG] task2 done
// [INFO] done

@since2.0.0

make
(({
message: unknown
message
}) =>
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err

@seesource

console
.
Console.log(message?: any, ...optionalParams: any[]): void

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout

See util.format() for more information.

@sincev0.1.100

log
(
message: unknown
message
))
)
// Create a custom runtime from the configuration layer
const
const runtime: ManagedRuntime.ManagedRuntime<never, never>
runtime
=
import ManagedRuntime
ManagedRuntime
.
const make: <never, never>(layer: Layer<never, never, never>, memoMap?: MemoMap | undefined) => ManagedRuntime.ManagedRuntime<never, never>

Convert a Layer into an ManagedRuntime, that can be used to run Effect's using your services.

@since2.0.0

@example

import { Console, Effect, Layer, ManagedRuntime } from "effect"
class Notifications extends Effect.Tag("Notifications")<
Notifications,
{ readonly notify: (message: string) => Effect.Effect<void> }
>() {
static Live = Layer.succeed(this, { notify: (message) => Console.log(message) })
}
async function main() {
const runtime = ManagedRuntime.make(Notifications.Live)
await runtime.runPromise(Notifications.notify("Hello, world!"))
await runtime.dispose()
}
main()

make
(
const appLayer: Layer<never, never, never>
appLayer
)
const
const program: Effect.Effect<void, never, never>
program
=
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
("Application started!")
// Execute the program using the custom runtime
const runtime: ManagedRuntime.ManagedRuntime<never, never>
runtime
.
ManagedRuntime<never, never>.runSync: <void, never>(effect: Effect.Effect<void, never, never>) => void

Executes the effect synchronously throwing in case of errors or async boundaries.

This method is effectful and should only be invoked at the edges of your program.

runSync
(
const program: Effect.Effect<void, never, never>
program
)
// Clean up resources associated with the custom runtime
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const runFork: <void, never>(effect: Effect.Effect<void, never, never>, options?: RunForkOptions) => RuntimeFiber<void, never>

The foundational function for running effects, returning a "fiber" that can be observed or interrupted.

When to Use

runFork is used to run an effect in the background by creating a fiber. It is the base function for all other run functions. It starts a fiber that can be observed or interrupted.

Unless you specifically need a Promise or synchronous operation, runFork is a good default choice.

@example

// Title: Running an Effect in the Background
import { Effect, Console, Schedule, Fiber } from "effect"
// ┌─── Effect<number, never, never>
// ▼
const program = Effect.repeat(
Console.log("running..."),
Schedule.spaced("200 millis")
)
// ┌─── RuntimeFiber<number, never>
// ▼
const fiber = Effect.runFork(program)
setTimeout(() => {
Effect.runFork(Fiber.interrupt(fiber))
}, 500)

@since2.0.0

runFork
(
const runtime: ManagedRuntime.ManagedRuntime<never, never>
runtime
.
ManagedRuntime<never, never>.disposeEffect: Effect.Effect<void, never, never>

Dispose of the resources associated with the runtime.

disposeEffect
)
/*
Output:
[ 'Application started!' ]
*/

When working with runtimes that you pass around, Effect.Tag can help simplify the access to services. It lets you define a new tag and embed the service shape directly into the static properties of the tag class.

Example (Defining a Tag for Notifications)

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
} from "effect"
class
class Notifications
Notifications
extends
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const Tag: <"Notifications">(id: "Notifications") => <Self, Type>() => TagClass<Self, "Notifications", Type> & (Type extends Record<PropertyKey, any> ? Effect.Tag.Proxy<...> : {}) & {
...;
}

@example

import { Effect, Layer } from "effect"
class MapTag extends Effect.Tag("MapTag")<MapTag, Map<string, string>>() {
static Live = Layer.effect(
this,
Effect.sync(() => new Map())
)
}

@since2.0.0

Tag
("Notifications")<
class Notifications
Notifications
,
{ readonly
notify: (message: string) => Effect.Effect<void>
notify
: (
message: string
message
: string) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
interface Effect<out A, out E = never, out R = never>

The Effect interface defines a value that lazily describes a workflow or job. The workflow requires some context R, and may fail with an error of type E, or succeed with a value of type A.

Effect values model resourceful interaction with the outside world, including synchronous, asynchronous, concurrent, and parallel interaction. They use a fiber-based concurrency model, with built-in support for scheduling, fine-grained interruption, structured concurrency, and high scalability.

To run an Effect value, you need a Runtime, which is a type that is capable of executing Effect values.

@since2.0.0

@since2.0.0

Effect
<void> }
>() {}

In this setup, the fields of the service (in this case, the notify method) are turned into static properties of the Notifications class, making it easier to access them.

This allows you to interact with the service directly:

Example (Using the Notifications Tag)

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
} from "effect"
class
class Notifications
Notifications
extends
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const Tag: <"Notifications">(id: "Notifications") => <Self, Type>() => TagClass<Self, "Notifications", Type> & (Type extends Record<PropertyKey, any> ? Effect.Tag.Proxy<...> : {}) & {
...;
}

@example

import { Effect, Layer } from "effect"
class MapTag extends Effect.Tag("MapTag")<MapTag, Map<string, string>>() {
static Live = Layer.effect(
this,
Effect.sync(() => new Map())
)
}

@since2.0.0

Tag
("Notifications")<
class Notifications
Notifications
,
{ readonly
notify: (message: string) => Effect.Effect<void>
notify
: (
message: string
message
: string) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
interface Effect<out A, out E = never, out R = never>

The Effect interface defines a value that lazily describes a workflow or job. The workflow requires some context R, and may fail with an error of type E, or succeed with a value of type A.

Effect values model resourceful interaction with the outside world, including synchronous, asynchronous, concurrent, and parallel interaction. They use a fiber-based concurrency model, with built-in support for scheduling, fine-grained interruption, structured concurrency, and high scalability.

To run an Effect value, you need a Runtime, which is a type that is capable of executing Effect values.

@since2.0.0

@since2.0.0

Effect
<void> }
>() {}
// Create an effect that depends on the Notifications service
const action =
class Notifications
Notifications
.
notify: (message: string) => Effect.Effect<void, never, Notifications>
notify
("Hello, world!")
const action: Effect.Effect<void, never, Notifications>

In this example, the action effect depends on the Notifications service. This approach allows you to reference services without manually passing them around. Later, you can create a Layer that provides the Notifications service and build a ManagedRuntime with that layer to ensure the service is available where needed.

The ManagedRuntime simplifies the integration of services and layers with other frameworks or tools, particularly in environments where Effect is not the primary framework and access to the main entry point is restricted.

For example, in environments like React or other frameworks where you have limited control over the main application entry point, ManagedRuntime helps manage the lifecycle of services.

Here’s how to manage a service’s lifecycle within an external framework:

Example (Using ManagedRuntime in an External Framework)

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import ManagedRuntime
ManagedRuntime
,
import Layer
Layer
,
import Console
Console
} from "effect"
// Define the Notifications service using Effect.Tag
class
class Notifications
Notifications
extends
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const Tag: <"Notifications">(id: "Notifications") => <Self, Type>() => TagClass<Self, "Notifications", Type> & (Type extends Record<PropertyKey, any> ? Effect.Tag.Proxy<...> : {}) & {
...;
}

@example

import { Effect, Layer } from "effect"
class MapTag extends Effect.Tag("MapTag")<MapTag, Map<string, string>>() {
static Live = Layer.effect(
this,
Effect.sync(() => new Map())
)
}

@since2.0.0

Tag
("Notifications")<
class Notifications
Notifications
,
{ readonly
notify: (message: string) => Effect.Effect<void>
notify
: (
message: string
message
: string) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
interface Effect<out A, out E = never, out R = never>

The Effect interface defines a value that lazily describes a workflow or job. The workflow requires some context R, and may fail with an error of type E, or succeed with a value of type A.

Effect values model resourceful interaction with the outside world, including synchronous, asynchronous, concurrent, and parallel interaction. They use a fiber-based concurrency model, with built-in support for scheduling, fine-grained interruption, structured concurrency, and high scalability.

To run an Effect value, you need a Runtime, which is a type that is capable of executing Effect values.

@since2.0.0

@since2.0.0

Effect
<void> }
>() {
// Provide a live implementation of the Notifications service
static
Notifications.Live: Layer.Layer<Notifications, never, never>
Live
=
import Layer
Layer
.
const succeed: <typeof Notifications>(tag: typeof Notifications, resource: {
readonly notify: (message: string) => Effect.Effect<void>;
}) => Layer.Layer<Notifications, never, never> (+1 overload)

Constructs a layer from the specified value.

@since2.0.0

succeed
(this, {
notify: (message: string) => Effect.Effect<void>
notify
: (
message: string
message
) =>
import Console
Console
.
const log: (...args: ReadonlyArray<any>) => Effect.Effect<void>

@since2.0.0

log
(
message: string
message
)
})
}
// Example entry point for an external framework
async function
function main(): Promise<void>
main
() {
// Create a custom runtime using the Notifications layer
const
const runtime: ManagedRuntime.ManagedRuntime<Notifications, never>
runtime
=
import ManagedRuntime
ManagedRuntime
.
const make: <Notifications, never>(layer: Layer.Layer<Notifications, never, never>, memoMap?: Layer.MemoMap | undefined) => ManagedRuntime.ManagedRuntime<Notifications, never>

Convert a Layer into an ManagedRuntime, that can be used to run Effect's using your services.

@since2.0.0

@example

import { Console, Effect, Layer, ManagedRuntime } from "effect"
class Notifications extends Effect.Tag("Notifications")<
Notifications,
{ readonly notify: (message: string) => Effect.Effect<void> }
>() {
static Live = Layer.succeed(this, { notify: (message) => Console.log(message) })
}
async function main() {
const runtime = ManagedRuntime.make(Notifications.Live)
await runtime.runPromise(Notifications.notify("Hello, world!"))
await runtime.dispose()
}
main()

make
(
class Notifications
Notifications
.
Notifications.Live: Layer.Layer<Notifications, never, never>
Live
)
// Run the effect
await
const runtime: ManagedRuntime.ManagedRuntime<Notifications, never>
runtime
.
ManagedRuntime<Notifications, never>.runPromise: <void, never>(effect: Effect.Effect<void, never, Notifications>, options?: {
readonly signal?: AbortSignal | undefined;
} | undefined) => Promise<void>

Runs the Effect, returning a JavaScript Promise that will be resolved with the value of the effect once the effect has been executed, or will be rejected with the first error or exception throw by the effect.

This method is effectful and should only be used at the edges of your program.

runPromise
(
class Notifications
Notifications
.
notify: (message: string) => Effect.Effect<void, never, Notifications>
notify
("Hello, world!"))
// Dispose of the runtime, cleaning up resources
await
const runtime: ManagedRuntime.ManagedRuntime<Notifications, never>
runtime
.
ManagedRuntime<Notifications, never>.dispose: () => Promise<void>

Dispose of the resources associated with the runtime.

dispose
()
}