Skip to content

管理服务

在编程的上下文中,服务指的是可以被应用程序不同部分使用的可重用组件或功能。 服务旨在提供特定的能力,并且可以在多个模块或组件之间共享。

服务通常封装应用程序不同部分所需的常见任务或操作。 它们可以处理复杂操作、与外部系统或 API 交互、管理数据或执行其他专门任务。

服务通常设计为模块化的,与应用程序的其余部分解耦。 这使得它们可以轻松维护、测试和替换,而不会影响应用程序的整体功能。

在深入研究服务及其在应用程序开发中的集成时,从函数管理和依赖处理的基本原则开始是有帮助的,而不依赖于高级构造。想象一下必须手动将服务传递给每个需要它的函数:

const processData = (data: Data, databaseService: DatabaseService) => {
// Operations using the database service
}

随着应用程序的增长,这种方法变得繁琐且难以管理,服务需要通过多层函数传递。

为了简化这一点,您可能会考虑使用捆绑各种服务的环境对象:

type Context = {
databaseService: DatabaseService
loggingService: LoggingService
}
const processData = (data: Data, context: Context) => {
// Using multiple services from the context
}

然而,这引入了新的复杂性:您必须确保在使用环境之前正确设置所有必要的服务,这可能导致紧密耦合的代码,并使函数组合和测试变得更加困难。

Effect 库通过利用类型系统简化了这些依赖关系的管理。 不需要手动传递服务或环境对象,Effect 允许您使用 Effect 类型中的 Requirements 参数直接在函数的类型签名中声明服务依赖关系:

┌─── 表示所需的依赖关系
Effect<Success, Error, Requirements>

在使用 Effect 时,它在实践中的工作方式如下:

依赖声明:您直接在函数的类型中指定函数需要哪些服务,将依赖管理的复杂性推入类型系统。

服务提供Effect.provideService 用于使服务实现对需要它的函数可用。通过在开始时提供服务,您确保应用程序的所有部分都能一致地访问所需的服务,从而维护清洁和解耦的架构。

这种方法抽象了手动服务处理,让开发者专注于业务逻辑,而编译器确保所有依赖关系都得到正确管理。它还使代码更易于维护和扩展。

让我们逐步了解在 Effect 中管理服务:

  1. 创建服务:定义具有独特功能和接口的服务。
  2. 使用服务:在应用程序的函数中访问和利用服务。
  3. 提供服务实现:提供服务的实际实现以满足声明的需求。

到目前为止,我们使用 Effect 框架的示例都处理独立于外部服务运行的 effect。 这意味着我们 Effect 类型签名中的 Requirements 参数被设置为 never,表示没有依赖关系。

然而,现实世界的应用程序通常需要依赖特定服务才能正确运行的 effect。这些服务通过称为 Context 的构造进行管理和访问。

Context 作为 effect 可能需要的所有服务的存储库或容器。 它就像一个维护这些服务的存储,允许应用程序的各个部分根据需要访问和使用它们。

存储在 Context 中的服务直接反映在 Effect 类型的 Requirements 参数中。 Context 中的每个服务都由唯一的”标签”标识,这本质上是服务的唯一标识符。

当 effect 需要使用特定服务时,服务的标签包含在 Requirements 类型参数中。

要创建新服务,您需要两样东西:

  1. 唯一的标识符
  2. 描述服务可能操作的类型

示例(定义随机数生成器服务)

让我们创建一个用于生成随机数的服务。

  1. 标识符。我们将使用字符串 "MyRandomService" 作为唯一标识符。
  2. 类型。服务类型将有一个名为 next 的操作,返回一个随机数。
import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Context

@since2.0.0

@since2.0.0

Context
} from "effect"
// Declaring a tag for a service that generates random numbers
class
class Random
Random
extends
import Context

@since2.0.0

@since2.0.0

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

@example

import * as assert from "node:assert"
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
("MyRandomService")<
class Random
Random
,
{ readonly
next: Effect.Effect<number, never, never>
next
:
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 describes a workflow or job, which can succeed or fail.

Details

The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

@since2.0.0

@since2.0.0

Effect
<number> }
>() {}

导出的 Random 值在 Effect 中被称为标签。它作为服务的表示,允许 Effect 在运行时定位和使用此服务。

服务将存储在名为 Context 的集合中,可以将其视为键为标签、值为服务的 Map

type Context = Map<Tag, Service>

让我们总结一下到目前为止涵盖的概念:

概念描述
服务提供特定功能的可重用组件,在应用程序的不同部分中使用。
标签表示服务的唯一标识符,允许 Effect 定位和使用它。
上下文存储服务的集合,功能类似于以标签为键、服务为值的映射。

现在我们已经定义了服务标签,让我们通过构建一个简单的程序来看看如何使用它。

示例(使用随机数服务)

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Context

@since2.0.0

@since2.0.0

Context
} from "effect"
// Declaring a tag for a service that generates random numbers
class
class Random
Random
extends
import Context

@since2.0.0

@since2.0.0

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

@example

import * as assert from "node:assert"
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
("MyRandomService")<
class Random
Random
,
{ readonly
next: Effect.Effect<number, never, never>
next
:
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 describes a workflow or job, which can succeed or fail.

Details

The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

@since2.0.0

@since2.0.0

Effect
<number> }
>() {}
// 使用服务
//
// ┌─── Effect<void, never, Random>
// ▼
const
const program: Effect.Effect<void, never, Random>
program
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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

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

When to Use

Effect.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 random: {
readonly next: Effect.Effect<number>;
}
random
= yield*
class Random
Random
const
const randomNumber: number
randomNumber
= yield*
const random: {
readonly next: Effect.Effect<number>;
}
random
.
next: Effect.Effect<number, never, never>
next
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
(`random number: ${
const randomNumber: number
randomNumber
}`)
})

在上面的代码中,我们可以观察到我们能够像 effect 本身一样 yield Random 标签。 这允许我们访问服务的 next 操作。

值得注意的是,program 变量的类型在 Requirements 类型参数中包含 Random

const program: Effect<void, never, Random>

This indicates that our program requires the Random service to be provided in order to execute successfully.

If we attempt to execute the effect without providing the necessary service we will encounter a type-checking error:

Example (Type Error Without Service Provision)

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Context

@since2.0.0

@since2.0.0

Context
} from "effect"
// Declaring a tag for a service that generates random numbers
class
class Random
Random
extends
import Context

@since2.0.0

@since2.0.0

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

@example

import * as assert from "node:assert"
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
("MyRandomService")<
class Random
Random
,
{ readonly
next: Effect.Effect<number, never, never>
next
:
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 describes a workflow or job, which can succeed or fail.

Details

The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

@since2.0.0

@since2.0.0

Effect
<number> }
>() {}
// Using the service
const
const program: Effect.Effect<void, never, Random>
program
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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

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

When to Use

Effect.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 random: {
readonly next: Effect.Effect<number>;
}
random
= yield*
class Random
Random
const
const randomNumber: number
randomNumber
= yield*
const random: {
readonly next: Effect.Effect<number>;
}
random
.
next: Effect.Effect<number, never, never>
next
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
(`random number: ${
const randomNumber: number
randomNumber
}`)
})
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.

Details

This function evaluates the provided effect synchronously, returning its result directly. It is ideal for effects that do not fail or include asynchronous operations. If the effect does fail or involves async tasks, it will throw an error. Execution stops at the point of failure or asynchronous operation, making it unsuitable for effects that require asynchronous handling.

Important: Attempting to run effects that involve asynchronous operations or failures will result in exceptions being thrown, so use this function with care for purely synchronous and error-free effects.

When to Use

Use this function when:

  • You are sure that the effect will not fail or involve asynchronous operations.
  • You need a direct, synchronous result from the effect.
  • You are working within a context where asynchronous effects are not allowed.

Avoid using this function for effects that can fail or require asynchronous handling. For such cases, consider using

runPromise

or

runSyncExit

.

Example (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 (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

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

@since2.0.0

runSync
(program)
Error ts(2379) ― Argument of type 'Effect<void, never, Random>' is not assignable to parameter of type 'Effect<void, never, never>' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties. Type 'Random' is not assignable to type 'never'.

To resolve this error and successfully execute the program, we need to provide an actual implementation of the Random service.

In the next section, we will explore how to implement and provide the Random service to our program, enabling us to run it successfully.

In order to provide an actual implementation of the Random service, we can utilize the Effect.provideService function.

Example (Providing a Random Number Implementation)

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Context

@since2.0.0

@since2.0.0

Context
} from "effect"
// Declaring a tag for a service that generates random numbers
class
class Random
Random
extends
import Context

@since2.0.0

@since2.0.0

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

@example

import * as assert from "node:assert"
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
("MyRandomService")<
class Random
Random
,
{ readonly
next: Effect.Effect<number, never, never>
next
:
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 describes a workflow or job, which can succeed or fail.

Details

The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

@since2.0.0

@since2.0.0

Effect
<number> }
>() {}
// Using the service
const
const program: Effect.Effect<void, never, Random>
program
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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

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

When to Use

Effect.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 random: {
readonly next: Effect.Effect<number>;
}
random
= yield*
class Random
Random
const
const randomNumber: number
randomNumber
= yield*
const random: {
readonly next: Effect.Effect<number>;
}
random
.
next: Effect.Effect<number, never, never>
next
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
(`random number: ${
const randomNumber: number
randomNumber
}`)
})
// Providing the implementation
//
// ┌─── Effect<void, never, never>
// ▼
const
const runnable: Effect.Effect<void, never, never>
runnable
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const provideService: <void, never, Random, Random, {
readonly next: Effect.Effect<number>;
}>(self: Effect.Effect<void, never, Random>, tag: Context.Tag<Random, {
readonly next: Effect.Effect<number>;
}>, service: {
readonly next: Effect.Effect<number>;
}) => Effect.Effect<void, never, never> (+1 overload)

Provides an implementation for a service in the context of an effect.

Details

This function allows you to supply a specific implementation for a service required by an effect. Services are typically defined using Context.Tag, which acts as a unique identifier for the service. By using this function, you link the service to its concrete implementation, enabling the effect to execute successfully without additional requirements.

For example, you can use this function to provide a random number generator, a logger, or any other service your effect depends on. Once the service is provided, all parts of the effect that rely on the service will automatically use the implementation you supplied.

Example

import { Effect, Context } from "effect"
// Declaring a tag for a service that generates random numbers
class Random extends Context.Tag("MyRandomService")<
Random,
{ readonly next: Effect.Effect<number> }
>() {}
// Using the service
const program = Effect.gen(function* () {
const random = yield* Random
const randomNumber = yield* random.next
console.log(`random number: ${randomNumber}`)
})
// Providing the implementation
//
// ┌─── Effect<void, never, never>
// ▼
const runnable = Effect.provideService(program, Random, {
next: Effect.sync(() => Math.random())
})
// Run successfully
Effect.runPromise(runnable)
// Example Output:
// random number: 0.8241872233134417

@seeprovide for providing multiple layers to an effect.

@since2.0.0

provideService
(
const program: Effect.Effect<void, never, Random>
program
,
class Random
Random
, {
next: Effect.Effect<number, never, never>
next
:
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const sync: <number>(thunk: LazyArg<number>) => Effect.Effect<number, never, never>

Creates an Effect that represents a synchronous side-effectful computation.

Details

The provided function (thunk) must not throw errors; if it does, the error will be treated as a "defect".

This defect is not a standard error but indicates a flaw in the logic that was expected to be error-free. You can think of it similar to an unexpected crash in the program, which can be further managed or logged using tools like

catchAllDefect

.

When to Use

Use this function when you are sure the operation will not fail.

Example (Logging a Message)

import { Effect } from "effect"
const log = (message: string) =>
Effect.sync(() => {
console.log(message) // side effect
})
// ┌─── Effect<void, never, never>
// ▼
const program = log("Hello, World!")

@seetry_try for a version that can handle failures.

@since2.0.0

sync
(() =>
var Math: Math

An intrinsic object that provides basic mathematics functionality and constants.

Math
.
Math.random(): number

Returns a pseudorandom number between 0 and 1.

random
())
})
// Run successfully
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;
} | undefined) => Promise<void>

Executes an effect and returns the result as a Promise.

Details

This function runs an effect and converts its result into a Promise. If the effect succeeds, the Promise will resolve with the successful result. If the effect fails, the Promise will reject with an error, which includes the failure details of the effect.

The optional options parameter allows you to pass an AbortSignal for cancellation, enabling more fine-grained control over asynchronous tasks.

When to Use

Use this function when you need to execute an effect and work with its result in a promise-based system, such as when integrating with third-party libraries that expect Promise results.

Example (Running a Successful Effect as a Promise)

import { Effect } from "effect"
Effect.runPromise(Effect.succeed(1)).then(console.log)
// Output: 1

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

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

@since2.0.0

runPromise
(
const runnable: Effect.Effect<void, never, never>
runnable
)
/*
Example Output:
random number: 0.8241872233134417
*/

In the code above, we provide the program we defined earlier with an implementation of the Random service.

We use the Effect.provideService function to associate the Random tag with its implementation, an object with a next operation that generates a random number.

Notice that the Requirements type parameter of the runnable effect is now never. This indicates that the effect no longer requires any service to be provided.

With the implementation of the Random service in place, we are able to run the program without any further requirements.

To retrieve the service type from a tag, use the Context.Tag.Service utility type.

Example (Extracting Service Type)

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Context

@since2.0.0

@since2.0.0

Context
} from "effect"
// Declaring a tag
class
class Random
Random
extends
import Context

@since2.0.0

@since2.0.0

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

@example

import * as assert from "node:assert"
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
("MyRandomService")<
class Random
Random
,
{ readonly
next: Effect.Effect<number, never, never>
next
:
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 describes a workflow or job, which can succeed or fail.

Details

The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

@since2.0.0

@since2.0.0

Effect
<number> }
>() {}
// Extracting the type
type
type RandomShape = {
readonly next: Effect.Effect<number>;
}
RandomShape
=
import Context

@since2.0.0

@since2.0.0

Context
.
namespace Tag

@since3.5.9

@since2.0.0

@example

import * as assert from "node:assert"
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
.
type Tag<in out Id, in out Value>.Service<T extends Context.Tag<any, any> | Context.TagClassShape<any, any>> = T extends Context.Tag<any, any> ? T["Service"] : T extends Context.TagClassShape<any, infer A> ? A : never

@since2.0.0

Service
<
class Random
Random
>
/*
This is equivalent to:
type RandomShape = {
readonly next: Effect.Effect<number>;
}
*/

When we require the usage of more than one service, the process remains similar to what we’ve learned in defining a service, repeated for each service needed.

Example (Using Random and Logger Services)

Let’s examine an example where we need two services, namely Random and Logger:

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Context

@since2.0.0

@since2.0.0

Context
} from "effect"
// Declaring a tag for a service that generates random numbers
class
class Random
Random
extends
import Context

@since2.0.0

@since2.0.0

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

@example

import * as assert from "node:assert"
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
("MyRandomService")<
class Random
Random
,
{
readonly
next: Effect.Effect<number, never, never>
next
:
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 describes a workflow or job, which can succeed or fail.

Details

The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

@since2.0.0

@since2.0.0

Effect
<number>
}
>() {}
// Declaring a tag for the logging service
class
class Logger
Logger
extends
import Context

@since2.0.0

@since2.0.0

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

@example

import * as assert from "node:assert"
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
("MyLoggerService")<
class Logger
Logger
,
{
readonly
log: (message: string) => Effect.Effect<void>
log
: (
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 describes a workflow or job, which can succeed or fail.

Details

The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

@since2.0.0

@since2.0.0

Effect
<void>
}
>() {}
const
const program: Effect.Effect<void, never, Random | Logger>
program
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Context.Tag<Random, {
readonly next: Effect.Effect<number>;
}>> | YieldWrap<Context.Tag<Logger, {
readonly log: (message: string) => Effect.Effect<void>;
}>> | YieldWrap<Effect.Effect<void, never, never>>, void>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Context.Tag<Random, {
readonly next: Effect.Effect<number>;
}>> | YieldWrap<Context.Tag<Logger, {
readonly log: (message: string) => Effect.Effect<void>;
}>> | YieldWrap<...>, 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

Effect.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* () {
// Acquire instances of the 'Random' and 'Logger' services
const
const random: {
readonly next: Effect.Effect<number>;
}
random
= yield*
class Random
Random
const
const logger: {
readonly log: (message: string) => Effect.Effect<void>;
}
logger
= yield*
class Logger
Logger
const
const randomNumber: number
randomNumber
= yield*
const random: {
readonly next: Effect.Effect<number>;
}
random
.
next: Effect.Effect<number, never, never>
next
yield*
const logger: {
readonly log: (message: string) => Effect.Effect<void>;
}
logger
.
log: (message: string) => Effect.Effect<void>
log
(
var String: StringConstructor
(value?: any) => string

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

String
(
const randomNumber: number
randomNumber
))
})

The program effect now has a Requirements type parameter of Random | Logger:

const program: Effect<void, never, Random | Logger>

indicating that it requires both the Random and Logger services to be provided.

To execute the program, we need to provide implementations for both services:

Example (Providing Multiple Services)

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Context

@since2.0.0

@since2.0.0

Context
} from "effect"
22 collapsed lines
// Declaring a tag for a service that generates random numbers
class
class Random
Random
extends
import Context

@since2.0.0

@since2.0.0

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

@example

import * as assert from "node:assert"
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
("MyRandomService")<
class Random
Random
,
{
readonly
next: Effect.Effect<number, never, never>
next
:
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 describes a workflow or job, which can succeed or fail.

Details

The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

@since2.0.0

@since2.0.0

Effect
<number>
}
>() {}
// Declaring a tag for the logging service
class
class Logger
Logger
extends
import Context

@since2.0.0

@since2.0.0

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

@example

import * as assert from "node:assert"
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
("MyLoggerService")<
class Logger
Logger
,
{
readonly
log: (message: string) => Effect.Effect<void>
log
: (
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 describes a workflow or job, which can succeed or fail.

Details

The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

@since2.0.0

@since2.0.0

Effect
<void>
}
>() {}
const
const program: Effect.Effect<void, never, Random | Logger>
program
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Context.Tag<Logger, {
readonly log: (message: string) => Effect.Effect<void>;
}>> | YieldWrap<Context.Tag<Random, {
readonly next: Effect.Effect<number>;
}>> | YieldWrap<Effect.Effect<void, never, never>>, void>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Context.Tag<Logger, {
readonly log: (message: string) => Effect.Effect<void>;
}>> | YieldWrap<Context.Tag<Random, {
readonly next: Effect.Effect<number>;
}>> | YieldWrap<...>, 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

Effect.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 random: {
readonly next: Effect.Effect<number>;
}
random
= yield*
class Random
Random
const
const logger: {
readonly log: (message: string) => Effect.Effect<void>;
}
logger
= yield*
class Logger
Logger
const
const randomNumber: number
randomNumber
= yield*
const random: {
readonly next: Effect.Effect<number>;
}
random
.
next: Effect.Effect<number, never, never>
next
return yield*
const logger: {
readonly log: (message: string) => Effect.Effect<void>;
}
logger
.
log: (message: string) => Effect.Effect<void>
log
(
var String: StringConstructor
(value?: any) => string

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

String
(
const randomNumber: number
randomNumber
))
})
// Provide service implementations for 'Random' and 'Logger'
const
const runnable: Effect.Effect<void, never, never>
runnable
=
const program: Effect.Effect<void, never, Random | Logger>
program
.
Pipeable.pipe<Effect.Effect<void, never, Random | Logger>, Effect.Effect<void, never, Logger>, Effect.Effect<void, never, never>>(this: Effect.Effect<void, never, Random | Logger>, ab: (_: Effect.Effect<void, never, Random | Logger>) => Effect.Effect<void, never, Logger>, bc: (_: Effect.Effect<void, never, Logger>) => Effect.Effect<void, never, never>): Effect.Effect<void, never, never> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const provideService: <Random, {
readonly next: Effect.Effect<number>;
}>(tag: Context.Tag<Random, {
readonly next: Effect.Effect<number>;
}>, service: {
readonly next: Effect.Effect<number>;
}) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, Random>> (+1 overload)

Provides an implementation for a service in the context of an effect.

Details

This function allows you to supply a specific implementation for a service required by an effect. Services are typically defined using Context.Tag, which acts as a unique identifier for the service. By using this function, you link the service to its concrete implementation, enabling the effect to execute successfully without additional requirements.

For example, you can use this function to provide a random number generator, a logger, or any other service your effect depends on. Once the service is provided, all parts of the effect that rely on the service will automatically use the implementation you supplied.

Example

import { Effect, Context } from "effect"
// Declaring a tag for a service that generates random numbers
class Random extends Context.Tag("MyRandomService")<
Random,
{ readonly next: Effect.Effect<number> }
>() {}
// Using the service
const program = Effect.gen(function* () {
const random = yield* Random
const randomNumber = yield* random.next
console.log(`random number: ${randomNumber}`)
})
// Providing the implementation
//
// ┌─── Effect<void, never, never>
// ▼
const runnable = Effect.provideService(program, Random, {
next: Effect.sync(() => Math.random())
})
// Run successfully
Effect.runPromise(runnable)
// Example Output:
// random number: 0.8241872233134417

@seeprovide for providing multiple layers to an effect.

@since2.0.0

provideService
(
class Random
Random
, {
next: Effect.Effect<number, never, never>
next
:
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const sync: <number>(thunk: LazyArg<number>) => Effect.Effect<number, never, never>

Creates an Effect that represents a synchronous side-effectful computation.

Details

The provided function (thunk) must not throw errors; if it does, the error will be treated as a "defect".

This defect is not a standard error but indicates a flaw in the logic that was expected to be error-free. You can think of it similar to an unexpected crash in the program, which can be further managed or logged using tools like

catchAllDefect

.

When to Use

Use this function when you are sure the operation will not fail.

Example (Logging a Message)

import { Effect } from "effect"
const log = (message: string) =>
Effect.sync(() => {
console.log(message) // side effect
})
// ┌─── Effect<void, never, never>
// ▼
const program = log("Hello, World!")

@seetry_try for a version that can handle failures.

@since2.0.0

sync
(() =>
var Math: Math

An intrinsic object that provides basic mathematics functionality and constants.

Math
.
Math.random(): number

Returns a pseudorandom number between 0 and 1.

random
())
}),
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const provideService: <Logger, {
readonly log: (message: string) => Effect.Effect<void>;
}>(tag: Context.Tag<Logger, {
readonly log: (message: string) => Effect.Effect<void>;
}>, service: {
readonly log: (message: string) => Effect.Effect<void>;
}) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, Logger>> (+1 overload)

Provides an implementation for a service in the context of an effect.

Details

This function allows you to supply a specific implementation for a service required by an effect. Services are typically defined using Context.Tag, which acts as a unique identifier for the service. By using this function, you link the service to its concrete implementation, enabling the effect to execute successfully without additional requirements.

For example, you can use this function to provide a random number generator, a logger, or any other service your effect depends on. Once the service is provided, all parts of the effect that rely on the service will automatically use the implementation you supplied.

Example

import { Effect, Context } from "effect"
// Declaring a tag for a service that generates random numbers
class Random extends Context.Tag("MyRandomService")<
Random,
{ readonly next: Effect.Effect<number> }
>() {}
// Using the service
const program = Effect.gen(function* () {
const random = yield* Random
const randomNumber = yield* random.next
console.log(`random number: ${randomNumber}`)
})
// Providing the implementation
//
// ┌─── Effect<void, never, never>
// ▼
const runnable = Effect.provideService(program, Random, {
next: Effect.sync(() => Math.random())
})
// Run successfully
Effect.runPromise(runnable)
// Example Output:
// random number: 0.8241872233134417

@seeprovide for providing multiple layers to an effect.

@since2.0.0

provideService
(
class Logger
Logger
, {
log: (message: string) => Effect.Effect<void>
log
: (
message: string
message
) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const sync: <void>(thunk: LazyArg<void>) => Effect.Effect<void, never, never>

Creates an Effect that represents a synchronous side-effectful computation.

Details

The provided function (thunk) must not throw errors; if it does, the error will be treated as a "defect".

This defect is not a standard error but indicates a flaw in the logic that was expected to be error-free. You can think of it similar to an unexpected crash in the program, which can be further managed or logged using tools like

catchAllDefect

.

When to Use

Use this function when you are sure the operation will not fail.

Example (Logging a Message)

import { Effect } from "effect"
const log = (message: string) =>
Effect.sync(() => {
console.log(message) // side effect
})
// ┌─── Effect<void, never, never>
// ▼
const program = log("Hello, World!")

@seetry_try for a version that can handle failures.

@since2.0.0

sync
(() =>
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: string
message
))
})
)

Alternatively, instead of calling provideService multiple times, we can combine the service implementations into a single Context and then provide the entire context using the Effect.provide function:

Example (Combining Service Implementations)

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Context

@since2.0.0

@since2.0.0

Context
} from "effect"
22 collapsed lines
// Declaring a tag for a service that generates random numbers
class
class Random
Random
extends
import Context

@since2.0.0

@since2.0.0

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

@example

import * as assert from "node:assert"
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
("MyRandomService")<
class Random
Random
,
{
readonly
next: Effect.Effect<number, never, never>
next
:
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 describes a workflow or job, which can succeed or fail.

Details

The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

@since2.0.0

@since2.0.0

Effect
<number>
}
>() {}
// Declaring a tag for the logging service
class
class Logger
Logger
extends
import Context

@since2.0.0

@since2.0.0

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

@example

import * as assert from "node:assert"
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
("MyLoggerService")<
class Logger
Logger
,
{
readonly
log: (message: string) => Effect.Effect<void>
log
: (
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 describes a workflow or job, which can succeed or fail.

Details

The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

@since2.0.0

@since2.0.0

Effect
<void>
}
>() {}
const
const program: Effect.Effect<void, never, Random | Logger>
program
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Context.Tag<Logger, {
readonly log: (message: string) => Effect.Effect<void>;
}>> | YieldWrap<Context.Tag<Random, {
readonly next: Effect.Effect<number>;
}>> | YieldWrap<Effect.Effect<void, never, never>>, void>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Context.Tag<Logger, {
readonly log: (message: string) => Effect.Effect<void>;
}>> | YieldWrap<Context.Tag<Random, {
readonly next: Effect.Effect<number>;
}>> | YieldWrap<...>, 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

Effect.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 random: {
readonly next: Effect.Effect<number>;
}
random
= yield*
class Random
Random
const
const logger: {
readonly log: (message: string) => Effect.Effect<void>;
}
logger
= yield*
class Logger
Logger
const
const randomNumber: number
randomNumber
= yield*
const random: {
readonly next: Effect.Effect<number>;
}
random
.
next: Effect.Effect<number, never, never>
next
return yield*
const logger: {
readonly log: (message: string) => Effect.Effect<void>;
}
logger
.
log: (message: string) => Effect.Effect<void>
log
(
var String: StringConstructor
(value?: any) => string

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

String
(
const randomNumber: number
randomNumber
))
})
// Combine service implementations into a single 'Context'
const
const context: Context.Context<Random | Logger>
context
=
import Context

@since2.0.0

@since2.0.0

Context
.
const empty: () => Context.Context<never>

Returns an empty Context.

@example

import * as assert from "node:assert"
import { Context } from "effect"
assert.strictEqual(Context.isContext(Context.empty()), true)

@since2.0.0

empty
().
Pipeable.pipe<Context.Context<never>, Context.Context<Random>, Context.Context<Random | Logger>>(this: Context.Context<never>, ab: (_: Context.Context<never>) => Context.Context<Random>, bc: (_: Context.Context<Random>) => Context.Context<Random | Logger>): Context.Context<Random | Logger> (+21 overloads)
pipe
(
import Context

@since2.0.0

@since2.0.0

Context
.
const add: <Random, {
readonly next: Effect.Effect<number>;
}>(tag: Context.Tag<Random, {
readonly next: Effect.Effect<number>;
}>, service: {
readonly next: Effect.Effect<number>;
}) => <Services>(self: Context.Context<Services>) => Context.Context<Random | Services> (+1 overload)

Adds a service to a given Context.

@example

import * as assert from "node:assert"
import { Context, pipe } from "effect"
const Port = Context.GenericTag<{ PORT: number }>("Port")
const Timeout = Context.GenericTag<{ TIMEOUT: number }>("Timeout")
const someContext = Context.make(Port, { PORT: 8080 })
const Services = pipe(
someContext,
Context.add(Timeout, { TIMEOUT: 5000 })
)
assert.deepStrictEqual(Context.get(Services, Port), { PORT: 8080 })
assert.deepStrictEqual(Context.get(Services, Timeout), { TIMEOUT: 5000 })

@since2.0.0

add
(
class Random
Random
, {
next: Effect.Effect<number, never, never>
next
:
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const sync: <number>(thunk: LazyArg<number>) => Effect.Effect<number, never, never>

Creates an Effect that represents a synchronous side-effectful computation.

Details

The provided function (thunk) must not throw errors; if it does, the error will be treated as a "defect".

This defect is not a standard error but indicates a flaw in the logic that was expected to be error-free. You can think of it similar to an unexpected crash in the program, which can be further managed or logged using tools like

catchAllDefect

.

When to Use

Use this function when you are sure the operation will not fail.

Example (Logging a Message)

import { Effect } from "effect"
const log = (message: string) =>
Effect.sync(() => {
console.log(message) // side effect
})
// ┌─── Effect<void, never, never>
// ▼
const program = log("Hello, World!")

@seetry_try for a version that can handle failures.

@since2.0.0

sync
(() =>
var Math: Math

An intrinsic object that provides basic mathematics functionality and constants.

Math
.
Math.random(): number

Returns a pseudorandom number between 0 and 1.

random
()) }),
import Context

@since2.0.0

@since2.0.0

Context
.
const add: <Logger, {
readonly log: (message: string) => Effect.Effect<void>;
}>(tag: Context.Tag<Logger, {
readonly log: (message: string) => Effect.Effect<void>;
}>, service: {
readonly log: (message: string) => Effect.Effect<void>;
}) => <Services>(self: Context.Context<Services>) => Context.Context<Logger | Services> (+1 overload)

Adds a service to a given Context.

@example

import * as assert from "node:assert"
import { Context, pipe } from "effect"
const Port = Context.GenericTag<{ PORT: number }>("Port")
const Timeout = Context.GenericTag<{ TIMEOUT: number }>("Timeout")
const someContext = Context.make(Port, { PORT: 8080 })
const Services = pipe(
someContext,
Context.add(Timeout, { TIMEOUT: 5000 })
)
assert.deepStrictEqual(Context.get(Services, Port), { PORT: 8080 })
assert.deepStrictEqual(Context.get(Services, Timeout), { TIMEOUT: 5000 })

@since2.0.0

add
(
class Logger
Logger
, {
log: (message: string) => Effect.Effect<void>
log
: (
message: string
message
) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const sync: <void>(thunk: LazyArg<void>) => Effect.Effect<void, never, never>

Creates an Effect that represents a synchronous side-effectful computation.

Details

The provided function (thunk) must not throw errors; if it does, the error will be treated as a "defect".

This defect is not a standard error but indicates a flaw in the logic that was expected to be error-free. You can think of it similar to an unexpected crash in the program, which can be further managed or logged using tools like

catchAllDefect

.

When to Use

Use this function when you are sure the operation will not fail.

Example (Logging a Message)

import { Effect } from "effect"
const log = (message: string) =>
Effect.sync(() => {
console.log(message) // side effect
})
// ┌─── Effect<void, never, never>
// ▼
const program = log("Hello, World!")

@seetry_try for a version that can handle failures.

@since2.0.0

sync
(() =>
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: string
message
))
})
)
// Provide the entire context
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, Random | Logger, Random | Logger>(self: Effect.Effect<void, never, Random | Logger>, context: Context.Context<Random | Logger>) => Effect.Effect<void, never, never> (+9 overloads)

Provides necessary dependencies to an effect, removing its environmental requirements.

Details

This function allows you to supply the required environment for an effect. The environment can be provided in the form of one or more Layers, a Context, a Runtime, or a ManagedRuntime. Once the environment is provided, the effect can run without requiring external dependencies.

You can compose layers to create a modular and reusable way of setting up the environment for effects. For example, layers can be used to configure databases, logging services, or any other required dependencies.

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"
// []

@seeprovideService for providing a service to an effect.

@since2.0.0

provide
(
const program: Effect.Effect<void, never, Random | Logger>
program
,
const context: Context.Context<Random | Logger>
context
)

There are situations where we may want to access a service implementation only if it is available. In such cases, we can use the Effect.serviceOption function to handle this scenario.

The Effect.serviceOption function returns an implementation that is available only if it is actually provided before executing this effect. To represent this optionality it returns an Option of the implementation.

Example (Handling Optional Services)

To determine what action to take, we can use the Option.isNone function provided by the Option module. This function allows us to check if the service is available or not by returning true when the service is not available.

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Context

@since2.0.0

@since2.0.0

Context
,
import Option

@since2.0.0

@since2.0.0

Option
} from "effect"
// Declaring a tag for a service that generates random numbers
class
class Random
Random
extends
import Context

@since2.0.0

@since2.0.0

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

@example

import * as assert from "node:assert"
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
("MyRandomService")<
class Random
Random
,
{ readonly
next: Effect.Effect<number, never, never>
next
:
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 describes a workflow or job, which can succeed or fail.

Details

The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

@since2.0.0

@since2.0.0

Effect
<number> }
>() {}
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<Option.Option<{
readonly next: Effect.Effect<number>;
}>, never, never>> | YieldWrap<Effect.Effect<number, never, never>>, void>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<Option.Option<{
readonly next: Effect.Effect<number>;
}>, never, never>> | YieldWrap<Effect.Effect<number, never, never>>, void, never>) => Effect.Effect<void, never, never> (+1 overload)

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

When to Use

Effect.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 maybeRandom: Option.Option<{
readonly next: Effect.Effect<number>;
}>
maybeRandom
= yield*
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const serviceOption: <Random, {
readonly next: Effect.Effect<number>;
}>(tag: Context.Tag<Random, {
readonly next: Effect.Effect<number>;
}>) => Effect.Effect<Option.Option<{
readonly next: Effect.Effect<number>;
}>, never, never>

Retrieves an optional service from the context as an Option.

Details

This function retrieves a service from the context and wraps it in an Option. If the service is available, it returns a Some containing the service. If the service is not found, it returns a None. This approach is useful when you want to handle the absence of a service gracefully without causing an error.

When to Use

Use this function when:

  • You need to access a service that may or may not be present in the context.
  • You want to handle the absence of a service using the Option type instead of throwing an error.

@seeserviceOptional for a version that throws an error if the service is missing.

@since2.0.0

serviceOption
(
class Random
Random
)
const
const randomNumber: number
randomNumber
=
import Option

@since2.0.0

@since2.0.0

Option
.
const isNone: <{
readonly next: Effect.Effect<number>;
}>(self: Option.Option<{
readonly next: Effect.Effect<number>;
}>) => self is Option.None<{
readonly next: Effect.Effect<number>;
}>

Checks whether an Option represents the absence of a value (None).

@example

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

@seeisSome for the opposite check.

@since2.0.0

isNone
(
const maybeRandom: Option.Option<{
readonly next: Effect.Effect<number>;
}>
maybeRandom
)
? // the service is not available, return a default value
-1
: // the service is available
yield*
const maybeRandom: Option.Some<{
readonly next: Effect.Effect<number>;
}>
maybeRandom
.
Some<{ readonly next: Effect.Effect<number>; }>.value: {
readonly next: Effect.Effect<number>;
}
value
.
next: Effect.Effect<number, never, never>
next
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
(
const randomNumber: number
randomNumber
)
})

In the code above, we can observe that the Requirements type parameter of the program effect is never, even though we are working with a service. This allows us to access something from the context only if it is actually provided before executing this effect.

When we run the program effect without providing the Random service:

Effect.runPromise(program).then(console.log)
// Output: -1

We see that the log message contains -1, which is the default value we provided when the service was not available.

However, if we provide the Random service implementation:

Effect.runPromise(
Effect.provideService(program, Random, {
next: Effect.sync(() => Math.random())
})
).then(console.log)
// Example Output: 0.9957979486841035

We can observe that the log message now contains a random number generated by the next operation of the Random service.

Sometimes a service in your application may depend on other services. To maintain a clean architecture, it’s important to manage these dependencies without making them explicit in the service interface. Instead, you can use layers to handle these dependencies during the service construction phase.

Example (Defining a Logger Service with a Configuration Dependency)

Consider a scenario where multiple services depend on each other. In this case, the Logger service requires access to a configuration service (Config).

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Context

@since2.0.0

@since2.0.0

Context
} from "effect"
// Declaring a tag for the Config service
class
class Config
Config
extends
import Context

@since2.0.0

@since2.0.0

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

@example

import * as assert from "node:assert"
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
("Config")<
class Config
Config
, {}>() {}
// Declaring a tag for the logging service
class
class Logger
Logger
extends
import Context

@since2.0.0

@since2.0.0

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

@example

import * as assert from "node:assert"
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
("MyLoggerService")<
class Logger
Logger
,
{
// ❌ Avoid exposing Config as a requirement
readonly
log: (message: string) => Effect.Effect<void, never, Config>
log
: (
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 describes a workflow or job, which can succeed or fail.

Details

The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

@since2.0.0

@since2.0.0

Effect
<void, never,
class Config
Config
>
}
>() {}

To handle these dependencies in a structured way and prevent them from leaking into the service interfaces, you can use the Layer abstraction. For more details on managing dependencies with layers, refer to the Managing Layers page.