# Managing Layers
import { Aside } from "@astrojs/starlight/components"
In the [Managing Services](/docs/requirements-management/services/) page, you learned how to create effects which depend on some service to be provided in order to execute, as well as how to provide that service to an effect.
However, what if we have a service within our effect program that has dependencies on other services in order to be built? We want to avoid leaking these implementation details into the service interface.
To represent the "dependency graph" of our program and manage these dependencies more effectively, we can utilize a powerful abstraction called "Layer".
Layers act as **constructors for creating services**, allowing us to manage dependencies during construction rather than at the service level. This approach helps to keep our service interfaces clean and focused.
Let's review some key concepts before diving into the details:
| Concept | Description |
| ----------- | ------------------------------------------------------------------------------------------------------------------------- |
| **service** | A reusable component providing specific functionality, used across different parts of an application. |
| **tag** | A unique identifier representing a **service**, allowing Effect to locate and use it. |
| **context** | A collection storing services, functioning like a map with **tags** as keys and **services** as values. |
| **layer** | An abstraction for constructing **services**, managing dependencies during construction rather than at the service level. |
## Designing the Dependency Graph
Let's imagine that we are building a web application. We could imagine that the dependency graph for an application where we need to manage configuration, logging, and database access might look something like this:
- The `Config` service provides application configuration.
- The `Logger` service depends on the `Config` service.
- The `Database` service depends on both the `Config` and `Logger` services.
Our goal is to build the `Database` service along with its direct and indirect dependencies. This means we need to ensure that the `Config` service is available for both `Logger` and `Database`, and then provide these dependencies to the `Database` service.
## Avoiding Requirement Leakage
When constructing the `Database` service, it's important to avoid exposing the dependencies on `Config` and `Logger` within the `Database` interface.
You might be tempted to define the `Database` service as follows:
**Example** (Leaking Dependencies in the Service Interface)
```ts twoslash "Config | Logger"
import { Effect, Context } from "effect"
// Declaring a tag for the Config service
class Config extends Context.Tag("Config")<Config, {}>() {}
// Declaring a tag for the Logger service
class Logger extends Context.Tag("Logger")<Logger, {}>() {}
// Declaring a tag for the Database service
class Database extends Context.Tag("Database")<
Database,
{
// ❌ Avoid exposing Config and Logger as a requirement
readonly query: (
sql: string
) => Effect.Effect<unknown, never, Config | Logger>
}
>() {}
```
Here, the `query` function of the `Database` service requires both `Config` and `Logger`. This design leaks implementation details, making the `Database` service aware of its dependencies, which complicates testing and makes it difficult to mock.
<Aside type="tip" title="Keep Service Interfaces Simple">
Service functions should avoid requiring dependencies directly. In practice, service operations should have the `Requirements` parameter set to `never`:
```text showLineNumbers=false "never"
┌─── No dependencies required
▼
Effect<Success, Error, never>
```
</Aside>
To demonstrate the problem, let's create a test instance of the `Database` service:
**Example** (Creating a Test Instance with Leaked Dependencies)
```ts twoslash collapse={3-17}
import { Effect, Context } from "effect"
// Declaring a tag for the Config service
class Config extends Context.Tag("Config")<Config, {}>() {}
// Declaring a tag for the Logger service
class Logger extends Context.Tag("Logger")<Logger, {}>() {}
// Declaring a tag for the Database service
class Database extends Context.Tag("Database")<
Database,
{
readonly query: (
sql: string
) => Effect.Effect<unknown, never, Config | Logger>
}
>() {}
// Declaring a test instance of the Database service
const DatabaseTest = Database.of({
// Simulating a simple response
query: (sql: string) => Effect.succeed([])
})
import * as assert from "node:assert"
// A test that uses the Database service
const test = Effect.gen(function* () {
const database = yield* Database
const result = yield* database.query("SELECT * FROM users")
assert.deepStrictEqual(result, [])
})
// ┌─── Effect<unknown, never, Config | Logger>
// ▼
const incompleteTestSetup = test.pipe(
// Attempt to provide only the Database service without Config and Logger
Effect.provideService(Database, DatabaseTest)
)
```
Because the `Database` service interface directly includes dependencies on `Config` and `Logger`, it forces any test setup to include these services, even if they're irrelevant to the test. This adds unnecessary complexity and makes it difficult to write simple, isolated unit tests.
Instead of directly tying dependencies to the `Database` service interface, dependencies should be managed at the construction phase.
We can use **layers** to properly construct the `Database` service and manage its dependencies without leaking details into the interface.
<Aside type="tip" title="Use Layers for Dependencies">
When a service has its own requirements, it's best to separate
implementation details into layers. Layers act as **constructors for
creating the service**, allowing us to handle dependencies at the
construction level rather than the service level.
</Aside>
## Creating Layers
The `Layer` type is structured as follows:
```text showLineNumbers=false
┌─── The service to be created
│ ┌─── The possible error
│ │ ┌─── The required dependencies
▼ ▼ ▼
Layer<RequirementsOut, Error, RequirementsIn>
```
A `Layer` represents a blueprint for constructing a `RequirementsOut` (the service). It requires a `RequirementsIn` (dependencies) as input and may result in an error of type `Error` during the construction process.
| Parameter | Description |
| ----------------- | -------------------------------------------------------------------------- |
| `RequirementsOut` | The service or resource to be created. |
| `Error` | The type of error that might occur during the construction of the service. |
| `RequirementsIn` | The dependencies required to construct the service. |
By using layers, you can better organize your services, ensuring that their dependencies are clearly defined and separated from their implementation details.
For simplicity, let's assume that we won't encounter any errors during the value construction (meaning `Error = never`).
Now, let's determine how many layers we need to implement our dependency graph:
| Layer | Dependencies | Type |
| -------------- | ---------------------------------------------------------- | ------------------------------------------ |
| `ConfigLive` | The `Config` service does not depend on any other services | `Layer<Config>` |
| `LoggerLive` | The `Logger` service depends on the `Config` service | `Layer<Logger, never, Config>` |
| `DatabaseLive` | The `Database` service depends on `Config` and `Logger` | `Layer<Database, never, Config \| Logger>` |
<Aside type="tip" title="Naming Conventions">
A common convention when naming the `Layer` for a particular service is
to add a `Live` suffix for the "live" implementation and a `Test` suffix
for the "test" implementation. For example, for a `Database` service,
the `DatabaseLive` would be the layer you provide in your application
and the `DatabaseTest` would be the layer you provide in your tests.
</Aside>
When a service has multiple dependencies, they are represented as a **union type**. In our case, the `Database` service depends on both the `Config` and `Logger` services. Therefore, the type for the `DatabaseLive` layer will be:
```ts showLineNumbers=false "Config | Logger"
Layer<Database, never, Config | Logger>
```
### Config
The `Config` service does not depend on any other services, so `ConfigLive` will be the simplest layer to implement. Just like in the [Managing Services](/docs/requirements-management/services/) page, we must create a tag for the service. And because the service has no dependencies, we can create the layer directly using the `Layer.succeed` constructor:
```ts twoslash
import { Effect, Context, Layer } from "effect"
// Declaring a tag for the Config service
class Config extends Context.Tag("Config")<
Config,
{
readonly getConfig: Effect.Effect<{
readonly logLevel: string
readonly connection: string
}>
}
>() {}
// Layer<Config, never, never>
const ConfigLive = Layer.succeed(
Config,
Config.of({
getConfig: Effect.succeed({
logLevel: "INFO",
connection: "mysql://username:password@hostname:port/database_name"
})
})
)
```
Looking at the type of `ConfigLive` we can observe:
- `RequirementsOut` is `Config`, indicating that constructing the layer will produce a `Config` service
- `Error` is `never`, indicating that layer construction cannot fail
- `RequirementsIn` is `never`, indicating that the layer has no dependencies
Note that, to construct `ConfigLive`, we used the `Config.of`
constructor. However, this is merely a helper to ensure correct type inference
for the implementation. It's possible to skip this helper and construct the
implementation directly as a simple object:
```ts twoslash collapse={4-12}
import { Effect, Context, Layer } from "effect"
// Declaring a tag for the Config service
class Config extends Context.Tag("Config")<
Config,
{
readonly getConfig: Effect.Effect<{
readonly logLevel: string
readonly connection: string
}>
}
>() {}
// Layer<Config, never, never>
const ConfigLive = Layer.succeed(Config, {
getConfig: Effect.succeed({
logLevel: "INFO",
connection: "mysql://username:password@hostname:port/database_name"
})
})
```
### Logger
Now we can move on to the implementation of the `Logger` service, which depends on the `Config` service to retrieve some configuration.
Just like we did in the [Managing Services](/docs/requirements-management/services/#using-the-service) page, we can yield the `Config` tag to "extract" the service from the context.
Given that using the `Config` tag is an effectful operation, we use `Layer.effect` to create a layer from the resulting effect.
```ts twoslash collapse={4-20}
import { Effect, Context, Layer } from "effect"
// Declaring a tag for the Config service
class Config extends Context.Tag("Config")<
Config,
{
readonly getConfig: Effect.Effect<{
readonly logLevel: string
readonly connection: string
}>
}
>() {}
// Layer<Config, never, never>
const ConfigLive = Layer.succeed(Config, {
getConfig: Effect.succeed({
logLevel: "INFO",
connection: "mysql://username:password@hostname:port/database_name"
})
})
// Declaring a tag for the Logger service
class Logger extends Context.Tag("Logger")<
Logger,
{ readonly log: (message: string) => Effect.Effect<void> }
>() {}
// Layer<Logger, never, Config>
const LoggerLive = Layer.effect(
Logger,
Effect.gen(function* () {
const config = yield* Config
return {
log: (message) =>
Effect.gen(function* () {
const { logLevel } = yield* config.getConfig
console.log(`[${logLevel}] ${message}`)
})
}
})
)
```
Looking at the type of `LoggerLive`:
```ts showLineNumbers=false
Layer<Logger, never, Config>
```
we can observe that:
- `RequirementsOut` is `Logger`
- `Error` is `never`, indicating that layer construction cannot fail
- `RequirementsIn` is `Config`, indicating that the layer has a requirement
### Database
Finally, we can use our `Config` and `Logger` services to implement the `Database` service.
```ts twoslash collapse={4-20,23-41}
import { Effect, Context, Layer } from "effect"
// Declaring a tag for the Config service
class Config extends Context.Tag("Config")<
Config,
{
readonly getConfig: Effect.Effect<{
readonly logLevel: string
readonly connection: string
}>
}
>() {}
// Layer<Config, never, never>
const ConfigLive = Layer.succeed(Config, {
getConfig: Effect.succeed({
logLevel: "INFO",
connection: "mysql://username:password@hostname:port/database_name"
})
})
// Declaring a tag for the Logger service
class Logger extends Context.Tag("Logger")<
Logger,
{ readonly log: (message: string) => Effect.Effect<void> }
>() {}
// Layer<Logger, never, Config>
const LoggerLive = Layer.effect(
Logger,
Effect.gen(function* () {
const config = yield* Config
return {
log: (message) =>
Effect.gen(function* () {
const { logLevel } = yield* config.getConfig
console.log(`[${logLevel}] ${message}`)
})
}
})
)
// Declaring a tag for the Database service
class Database extends Context.Tag("Database")<
Database,
{ readonly query: (sql: string) => Effect.Effect<unknown> }
>() {}
// Layer<Database, never, Config | Logger>
const DatabaseLive = Layer.effect(
Database,
Effect.gen(function* () {
const config = yield* Config
const logger = yield* Logger
return {
query: (sql: string) =>
Effect.gen(function* () {
yield* logger.log(`Executing query: ${sql}`)
const { connection } = yield* config.getConfig
return { result: `Results from ${connection}` }
})
}
})
)
```
Looking at the type of `DatabaseLive`:
```ts showLineNumbers=false
Layer<Database, never, Config | Logger>
```
we can observe that the `RequirementsIn` type is `Config | Logger`, i.e., the `Database` service requires both `Config` and `Logger` services.
## Combining Layers
Layers can be combined in two primary ways: **merging** and **composing**.
### Merging Layers
Layers can be combined through merging using the `Layer.merge` function:
```ts twoslash
import { Layer } from "effect"
declare const layer1: Layer.Layer<"Out1", never, "In1">
declare const layer2: Layer.Layer<"Out2", never, "In2">
// Layer<"Out1" | "Out2", never, "In1" | "In2">
const merging = Layer.merge(layer1, layer2)
```
When we merge two layers, the resulting layer:
- requires all the services that both of them require (`"In1" | "In2"`).
- produces all services that both of them produce (`"Out1" | "Out2"`).
For example, in our web application above, we can merge our `ConfigLive` and `LoggerLive` layers into a single `AppConfigLive` layer, which retains the requirements of both layers (`never | Config = Config`) and the outputs of both layers (`Config | Logger`):
```ts twoslash collapse={4-20,23-41}
import { Effect, Context, Layer } from "effect"
// Declaring a tag for the Config service
class Config extends Context.Tag("Config")<
Config,
{
readonly getConfig: Effect.Effect<{
readonly logLevel: string
readonly connection: string
}>
}
>() {}
// Layer<Config, never, never>
const ConfigLive = Layer.succeed(Config, {
getConfig: Effect.succeed({
logLevel: "INFO",
connection: "mysql://username:password@hostname:port/database_name"
})
})
// Declaring a tag for the Logger service
class Logger extends Context.Tag("Logger")<
Logger,
{ readonly log: (message: string) => Effect.Effect<void> }
>() {}
// Layer<Logger, never, Config>
const LoggerLive = Layer.effect(
Logger,
Effect.gen(function* () {
const config = yield* Config
return {
log: (message) =>
Effect.gen(function* () {
const { logLevel } = yield* config.getConfig
console.log(`[${logLevel}] ${message}`)
})
}
})
)
// Layer<Config | Logger, never, Config>
const AppConfigLive = Layer.merge(ConfigLive, LoggerLive)
```
### Composing Layers
Layers can be composed using the `Layer.provide` function:
```ts twoslash
import { Layer } from "effect"
declare const inner: Layer.Layer<"OutInner", never, "InInner">
declare const outer: Layer.Layer<"InInner", never, "InOuter">
// Layer<"OutInner", never, "InOuter">
const composition = Layer.provide(inner, outer)
```
Sequential composition of layers implies that the output of one layer is supplied as the input for the inner layer,
resulting in a single layer with the requirements of the outer layer and the output of the inner.
Now we can compose the `AppConfigLive` layer with the `DatabaseLive` layer:
```ts twoslash collapse={4-20,23-41,44-64}
import { Effect, Context, Layer } from "effect"
// Declaring a tag for the Config service
class Config extends Context.Tag("Config")<
Config,
{
readonly getConfig: Effect.Effect<{
readonly logLevel: string
readonly connection: string
}>
}
>() {}
// Layer<Config, never, never>
const ConfigLive = Layer.succeed(Config, {
getConfig: Effect.succeed({
logLevel: "INFO",
connection: "mysql://username:password@hostname:port/database_name"
})
})
// Declaring a tag for the Logger service
class Logger extends Context.Tag("Logger")<
Logger,
{ readonly log: (message: string) => Effect.Effect<void> }
>() {}
// Layer<Logger, never, Config>
const LoggerLive = Layer.effect(
Logger,
Effect.gen(function* () {
const config = yield* Config
return {
log: (message) =>
Effect.gen(function* () {
const { logLevel } = yield* config.getConfig
console.log(`[${logLevel}] ${message}`)
})
}
})
)
// Declaring a tag for the Database service
class Database extends Context.Tag("Database")<
Database,
{ readonly query: (sql: string) => Effect.Effect<unknown> }
>() {}
// Layer<Database, never, Config | Logger>
const DatabaseLive = Layer.effect(
Database,
Effect.gen(function* () {
const config = yield* Config
const logger = yield* Logger
return {
query: (sql: string) =>
Effect.gen(function* () {
yield* logger.log(`Executing query: ${sql}`)
const { connection } = yield* config.getConfig
return { result: `Results from ${connection}` }
})
}
})
)
// Layer<Config | Logger, never, Config>
const AppConfigLive = Layer.merge(ConfigLive, LoggerLive)
// Layer<Database, never, never>
const MainLive = DatabaseLive.pipe(
// provides the config and logger to the database
Layer.provide(AppConfigLive),
// provides the config to AppConfigLive
Layer.provide(ConfigLive)
)
```
We obtained a `MainLive` layer that produces the `Database` service:
```ts showLineNumbers=false
Layer<Database, never, never>
```
This layer is the fully resolved layer for our application.
### Merging and Composing Layers
Let's say we want our `MainLive` layer to return both the `Config` and `Database` services. We can achieve this with `Layer.provideMerge`:
```ts twoslash collapse={4-19,22-39,42-61}
import { Effect, Context, Layer } from "effect"
// Declaring a tag for the Config service
class Config extends Context.Tag("Config")<
Config,
{
readonly getConfig: Effect.Effect<{
readonly logLevel: string
readonly connection: string
}>
}
>() {}
const ConfigLive = Layer.succeed(Config, {
getConfig: Effect.succeed({
logLevel: "INFO",
connection: "mysql://username:password@hostname:port/database_name"
})
})
// Declaring a tag for the Logger service
class Logger extends Context.Tag("Logger")<
Logger,
{ readonly log: (message: string) => Effect.Effect<void> }
>() {}
const LoggerLive = Layer.effect(
Logger,
Effect.gen(function* () {
const config = yield* Config
return {
log: (message) =>
Effect.gen(function* () {
const { logLevel } = yield* config.getConfig
console.log(`[${logLevel}] ${message}`)
})
}
})
)
// Declaring a tag for the Database service
class Database extends Context.Tag("Database")<
Database,
{ readonly query: (sql: string) => Effect.Effect<unknown> }
>() {}
const DatabaseLive = Layer.effect(
Database,
Effect.gen(function* () {
const config = yield* Config
const logger = yield* Logger
return {
query: (sql: string) =>
Effect.gen(function* () {
yield* logger.log(`Executing query: ${sql}`)
const { connection } = yield* config.getConfig
return { result: `Results from ${connection}` }
})
}
})
)
// Layer<Config | Logger, never, Config>
const AppConfigLive = Layer.merge(ConfigLive, LoggerLive)
// Layer<Config | Database, never, never>
const MainLive = DatabaseLive.pipe(
Layer.provide(AppConfigLive),
Layer.provideMerge(ConfigLive)
)
```
## Providing a Layer to an Effect
Now that we have assembled the fully resolved `MainLive` for our application,
we can provide it to our program to satisfy the program's requirements using `Effect.provide`:
```ts twoslash collapse={3-65}
import { Effect, Context, Layer } from "effect"
class Config extends Context.Tag("Config")<
Config,
{
readonly getConfig: Effect.Effect<{
readonly logLevel: string
readonly connection: string
}>
}
>() {}
const ConfigLive = Layer.succeed(Config, {
getConfig: Effect.succeed({
logLevel: "INFO",
connection: "mysql://username:password@hostname:port/database_name"
})
})
class Logger extends Context.Tag("Logger")<
Logger,
{ readonly log: (message: string) => Effect.Effect<void> }
>() {}
const LoggerLive = Layer.effect(
Logger,
Effect.gen(function* () {
const config = yield* Config
return {
log: (message) =>
Effect.gen(function* () {
const { logLevel } = yield* config.getConfig
console.log(`[${logLevel}] ${message}`)
})
}
})
)
class Database extends Context.Tag("Database")<
Database,
{ readonly query: (sql: string) => Effect.Effect<unknown> }
>() {}
const DatabaseLive = Layer.effect(
Database,
Effect.gen(function* () {
const config = yield* Config
const logger = yield* Logger
return {
query: (sql: string) =>
Effect.gen(function* () {
yield* logger.log(`Executing query: ${sql}`)
const { connection } = yield* config.getConfig
return { result: `Results from ${connection}` }
})
}
})
)
const AppConfigLive = Layer.merge(ConfigLive, LoggerLive)
const MainLive = DatabaseLive.pipe(
Layer.provide(AppConfigLive),
Layer.provide(ConfigLive)
)
// ┌─── 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, MainLive)
Effect.runPromise(runnable).then(console.log)
/*
Output:
[INFO] Executing query: SELECT * FROM users
{
result: 'Results from mysql://username:password@hostname:port/database_name'
}
*/
```
Note that the `runnable` requirements type is `never`, indicating that the program does not require any additional services to run.
## Converting a Layer to an Effect
Sometimes your entire application might be a Layer, for example, an HTTP server. You can convert that layer to an effect with `Layer.launch`. It constructs the layer and keeps it alive until interrupted.
**Example** (Launching an HTTP Server Layer)
```ts twoslash
import { Console, Context, Effect, Layer } from "effect"
class HTTPServer extends Context.Tag("HTTPServer")<HTTPServer, void>() {}
// Simulating an HTTP server
const server = Layer.effect(
HTTPServer,
// Log a message to simulate a server starting
Console.log("Listening on http://localhost:3000")
)
// Converts the layer to an effect and runs it
Effect.runFork(Layer.launch(server))
/*
Output:
Listening on http://localhost:3000
...
*/
```
## Tapping
The `Layer.tap` and `Layer.tapError` functions allow you to perform additional effects based on the success or failure of a layer. These operations do not modify the layer's signature but are useful for logging or performing side effects during layer construction.
- `Layer.tap`: Executes a specified effect when the layer is successfully acquired.
- `Layer.tapError`: Executes a specified effect when the layer fails to acquire.
**Example** (Logging Success and Failure During Layer Acquisition)
```ts twoslash
import { Config, Context, Effect, Layer, Console } from "effect"
class HTTPServer extends Context.Tag("HTTPServer")<HTTPServer, void>() {}
// Simulating an HTTP server
const server = Layer.effect(
HTTPServer,
Effect.gen(function* () {
const host = yield* Config.string("HOST")
console.log(`Listening on http://localhost:${host}`)
})
).pipe(
// Log a message if the layer acquisition succeeds
Layer.tap((ctx) =>
Console.log(`layer acquisition succeeded with:\n${ctx}`)
),
// Log a message if the layer acquisition fails
Layer.tapError((err) =>
Console.log(`layer acquisition failed with:\n${err}`)
)
)
Effect.runFork(Layer.launch(server))
/*
Output:
layer acquisition failed with:
(Missing data at HOST: "Expected HOST to exist in the process context")
*/
```
## Error Handling
When constructing layers, it is important to handle potential errors. The Effect library provides tools like `Layer.catchAll` and `Layer.orElse` to manage errors and define fallback layers in case of failure.
### catchAll
The `Layer.catchAll` function allows you to recover from errors during layer construction by specifying a fallback layer. This can be useful for handling specific error cases and ensuring the application can continue with an alternative setup.
**Example** (Recovering from Errors During Layer Construction)
```ts twoslash
import { Config, Context, Effect, Layer } from "effect"
class HTTPServer extends Context.Tag("HTTPServer")<HTTPServer, void>() {}
// Simulating an HTTP server
const server = Layer.effect(
HTTPServer,
Effect.gen(function* () {
const host = yield* Config.string("HOST")
console.log(`Listening on http://localhost:${host}`)
})
).pipe(
// Recover from errors during layer construction
Layer.catchAll((configError) =>
Layer.effect(
HTTPServer,
Effect.gen(function* () {
console.log(`Recovering from error:\n${configError}`)
console.log(`Listening on http://localhost:3000`)
})
)
)
)
Effect.runFork(Layer.launch(server))
/*
Output:
Recovering from error:
(Missing data at HOST: "Expected HOST to exist in the process context")
Listening on http://localhost:3000
...
*/
```
### orElse
The `Layer.orElse` function provides a simpler way to fall back to an alternative layer if the initial layer fails. Unlike `Layer.catchAll`, it does not receive the error as input. Use this when you only need to provide a default layer without reacting to specific errors.
**Example** (Fallback to an Alternative Layer)
```ts twoslash
import { Config, Context, Effect, Layer } from "effect"
class Database extends Context.Tag("Database")<Database, void>() {}
// Simulating a database connection
const postgresDatabaseLayer = Layer.effect(
Database,
Effect.gen(function* () {
const databaseConnectionString = yield* Config.string(
"CONNECTION_STRING"
)
console.log(
`Connecting to database with: ${databaseConnectionString}`
)
})
)
// Simulating an in-memory database connection
const inMemoryDatabaseLayer = Layer.effect(
Database,
Effect.gen(function* () {
console.log(`Connecting to in-memory database`)
})
)
// Fallback to in-memory database if PostgreSQL connection fails
const database = postgresDatabaseLayer.pipe(
Layer.orElse(() => inMemoryDatabaseLayer)
)
Effect.runFork(Layer.launch(database))
/*
Output:
Connecting to in-memory database
...
*/
```
## Simplifying Service Definitions with Effect.Service
The `Effect.Service` API provides a way to define a service in a single step, including its tag and layer.
It also allows specifying dependencies upfront, making service construction more straightforward.
### Defining a Service with Dependencies
The following example defines a `Cache` service that depends on a file system.
**Example** (Defining a Cache Service)
```ts twoslash
import { FileSystem } from "@effect/platform"
import { NodeFileSystem } from "@effect/platform-node"
import { Effect } from "effect"
// Define a Cache service
class Cache extends Effect.Service<Cache>()("app/Cache", {
// Define how to create the service
effect: Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem
const lookup = (key: string) => fs.readFileString(`cache/${key}`)
return { lookup } as const
}),
// Specify dependencies
dependencies: [NodeFileSystem.layer]
}) {}
```
### Using the Generated Layers
The `Effect.Service` API automatically generates layers for the service.
| Layer | Description |
| ---------------------------------- | --------------------------------------------------------------------------------- |
| `Cache.Default` | Provides the `Cache` service with its dependencies already included. |
| `Cache.DefaultWithoutDependencies` | Provides the `Cache` service but requires dependencies to be provided separately. |
```ts twoslash collapse={6-13}
import { FileSystem } from "@effect/platform"
import { NodeFileSystem } from "@effect/platform-node"
import { Effect } from "effect"
// Define a Cache service
class Cache extends Effect.Service<Cache>()("app/Cache", {
effect: Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem
const lookup = (key: string) => fs.readFileString(`cache/${key}`)
return { lookup } as const
}),
dependencies: [NodeFileSystem.layer]
}) {}
// Layer that includes all required dependencies
//
// ┌─── Layer<Cache>
// ▼
const layer = Cache.Default
// Layer without dependencies, requiring them to be provided externally
//
// ┌─── Layer.Layer<Cache, never, FileSystem>
// ▼
const layerNoDeps = Cache.DefaultWithoutDependencies
```
### Accessing the Service
A service created with `Effect.Service` can be accessed like any other Effect service.
**Example** (Accessing the Cache Service)
```ts twoslash collapse={6-13}
import { FileSystem } from "@effect/platform"
import { NodeFileSystem } from "@effect/platform-node"
import { Effect, Console } from "effect"
// Define a Cache service
class Cache extends Effect.Service<Cache>()("app/Cache", {
effect: Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem
const lookup = (key: string) => fs.readFileString(`cache/${key}`)
return { lookup } as const
}),
dependencies: [NodeFileSystem.layer]
}) {}
// Accessing the Cache Service
const program = Effect.gen(function* () {
const cache = yield* Cache
const data = yield* cache.lookup("my-key")
console.log(data)
}).pipe(Effect.catchAllCause((cause) => Console.log(cause)))
const runnable = program.pipe(Effect.provide(Cache.Default))
Effect.runFork(runnable)
/*
{
_id: 'Cause',
_tag: 'Fail',
failure: {
_tag: 'SystemError',
reason: 'NotFound',
module: 'FileSystem',
method: 'readFile',
pathOrDescriptor: 'cache/my-key',
syscall: 'open',
message: "ENOENT: no such file or directory, open 'cache/my-key'",
[Symbol(@effect/platform/Error/PlatformErrorTypeId)]: Symbol(@effect/platform/Error/PlatformErrorTypeId)
}
}
*/
```
Since this example uses `Cache.Default`, it interacts with the real file system. If the file does not exist, it results in an error.
### Injecting Test Dependencies
To test the program without depending on the real file system, we can inject a test file system using the `Cache.DefaultWithoutDependencies` layer.
**Example** (Using a Test File System)
```ts twoslash collapse={6-13,16-20}
import { FileSystem } from "@effect/platform"
import { NodeFileSystem } from "@effect/platform-node"
import { Effect, Console } from "effect"
// Define a Cache service
class Cache extends Effect.Service<Cache>()("app/Cache", {
effect: Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem
const lookup = (key: string) => fs.readFileString(`cache/${key}`)
return { lookup } as const
}),
dependencies: [NodeFileSystem.layer]
}) {}
// Accessing the Cache Service
const program = Effect.gen(function* () {
const cache = yield* Cache
const data = yield* cache.lookup("my-key")
console.log(data)
}).pipe(Effect.catchAllCause((cause) => Console.log(cause)))
// Create a test file system that always returns a fixed value
const FileSystemTest = FileSystem.layerNoop({
readFileString: () => Effect.succeed("File Content...")
})
const runnable = program.pipe(
Effect.provide(Cache.DefaultWithoutDependencies),
// Provide the mock file system
Effect.provide(FileSystemTest)
)
Effect.runFork(runnable)
// Output: File Content...
```
### Mocking the Service Directly
Alternatively, you can mock the `Cache` service itself instead of replacing its dependencies.
**Example** (Mocking the Cache Service)
```ts twoslash collapse={6-13,16-20}
import { FileSystem } from "@effect/platform"
import { NodeFileSystem } from "@effect/platform-node"
import { Effect, Console } from "effect"
// Define a Cache service
class Cache extends Effect.Service<Cache>()("app/Cache", {
effect: Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem
const lookup = (key: string) => fs.readFileString(`cache/${key}`)
return { lookup } as const
}),
dependencies: [NodeFileSystem.layer]
}) {}
// Accessing the Cache Service
const program = Effect.gen(function* () {
const cache = yield* Cache
const data = yield* cache.lookup("my-key")
console.log(data)
}).pipe(Effect.catchAllCause((cause) => Console.log(cause)))
// Create a mock implementation of Cache
const cache = new Cache({
lookup: () => Effect.succeed("Cache Content...")
})
// Provide the mock Cache service
const runnable = program.pipe(Effect.provideService(Cache, cache))
Effect.runFork(runnable)
// Output: File Content...
```
### Alternative Ways to Define a Service
The `Effect.Service` API supports multiple ways to define a service:
| Method | Description |
| --------- | -------------------------------------------------- |
| `effect` | Defines a service with an effectful constructor. |
| `sync` | Defines a service using a synchronous constructor. |
| `succeed` | Provides a static implementation of the service. |
| `scoped` | Creates a service with lifecycle management. |
**Example** (Defining a Service with a Static Implementation)
```ts twoslash
import { Effect, Random } from "effect"
class Sync extends Effect.Service<Sync>()("Sync", {
sync: () => ({
next: Random.nextInt
})
}) {}
// ┌─── Effect<void, never, Sync>
// ▼
const program = Effect.gen(function* () {
const sync = yield* Sync
const n = yield* sync.next
console.log(`The number is ${n}`)
})
Effect.runPromise(program.pipe(Effect.provide(Sync.Default)))
// Example Output: The number is 3858843290019673
```
**Example** (Managing a Service with Lifecycle Control)
```ts twoslash
import { Effect, Console } from "effect"
class Scoped extends Effect.Service<Scoped>()("Scoped", {
scoped: Effect.gen(function* () {
// Acquire the resource and ensure it is properly released
const resource = yield* Effect.acquireRelease(
Console.log("Aquiring...").pipe(Effect.as("foo")),
() => Console.log("Releasing...")
)
// Register a finalizer to run when the effect is completed
yield* Effect.addFinalizer(() => Console.log("Shutting down"))
return { resource }
})
}) {}
// ┌─── Effect<void, never, Scoped>
// ▼
const program = Effect.gen(function* () {
const resource = (yield* Scoped).resource
console.log(`The resource is ${resource}`)
})
Effect.runPromise(
program.pipe(
Effect.provide(
// ┌─── Layer<Scoped, never, never>
// ▼
Scoped.Default
)
)
)
/*
Aquiring...
The resource is foo
Shutting down
Releasing...
*/
```
The `Scoped.Default` layer does not require `Scope` as a dependency, since `Scoped` itself manages its lifecycle.
### Enabling Direct Method Access
By setting `accessors: true`, you can call service methods directly using the service tag instead of first extracting the service.
**Example** (Defining a Service with Direct Method Access)
```ts twoslash del={11-12} ins={13}
import { Effect, Random } from "effect"
class Sync extends Effect.Service<Sync>()("Sync", {
sync: () => ({
next: Random.nextInt
}),
accessors: true // Enables direct method access via the tag
}) {}
const program = Effect.gen(function* () {
// const sync = yield* Sync
// const n = yield* sync.next
const n = yield* Sync.next // No need to extract the service first
console.log(`The number is ${n}`)
})
Effect.runPromise(program.pipe(Effect.provide(Sync.Default)))
// Example Output: The number is 3858843290019673
```
<Aside type="caution" title="Limitation of Direct Method Access">
Direct method access does not work with generic methods.
</Aside>
### Effect.Service vs Context.Tag
Both `Effect.Service` and `Context.Tag` are ways to model services in the Effect ecosystem. They serve similar purposes but target different use-cases.
| Feature | Effect.Service | Context.Tag |
| ----------------------------------- | ------------------------------------------------------- | ----------------------------------------- |
| Tag creation | Generated for you (the class name acts as the tag) | You declare the tag manually |
| Default implementation | **Required** - supplied inline (`effect`, `sync`, etc.) | **Optional** - can be supplied later |
| Ready-made layers (`.Default`, ...) | Automatically generated | You build layers yourself |
| Best suited for | Application code with a clear runtime implementation | Library code or dynamically-scoped values |
| When no sensible default exists | Not ideal; you would still have to invent one | Preferred |
**Key points**
- **Less boilerplate:** `Effect.Service` is syntactic sugar over `Context.Tag` plus the accompanying layer and helpers.
- **Default required:**
A class that extends `Effect.Service` must declare **one** of the built-in constructors (`effect`, `sync`, `succeed`, or `scoped`). This baseline implementation becomes part of `MyService.Default`, so any code that imports the service can run without providing extra layers.
That is handy for app-level services where a sensible runtime implementation exists (logging, HTTP clients, real databases, and so on).
If your service is inherently contextual (for example, a per-request database handle) or you are writing a library that should not assume an implementation, prefer `Context.Tag`: you publish only the tag and let callers supply the layer that makes sense in their environment.
- **The class _is_ the tag:** When you create a class with `extends Effect.Service`, the class constructor itself acts as the tag. You can provide alternate implementations by supplying a value for that class when wiring layers:
```ts
const mock = new MyService({
/* mocked methods */
})
program.pipe(Effect.provideService(MyService, mock))
```
In the Managing Services page, you learned how to create effects which depend on some service to be provided in order to execute, as well as how to provide that service to an effect.
However, what if we have a service within our effect program that has dependencies on other services in order to be built? We want to avoid leaking these implementation details into the service interface.
To represent the “dependency graph” of our program and manage these dependencies more effectively, we can utilize a powerful abstraction called “Layer”.
Layers act as constructors for creating services, allowing us to manage dependencies during construction rather than at the service level. This approach helps to keep our service interfaces clean and focused.
Let’s review some key concepts before diving into the details:
Concept
Description
service
A reusable component providing specific functionality, used across different parts of an application.
tag
A unique identifier representing a service, allowing Effect to locate and use it.
context
A collection storing services, functioning like a map with tags as keys and services as values.
layer
An abstraction for constructing services, managing dependencies during construction rather than at the service level.
Designing the Dependency Graph
Let’s imagine that we are building a web application. We could imagine that the dependency graph for an application where we need to manage configuration, logging, and database access might look something like this:
The Config service provides application configuration.
The Logger service depends on the Config service.
The Database service depends on both the Config and Logger services.
Our goal is to build the Database service along with its direct and indirect dependencies. This means we need to ensure that the Config service is available for both Logger and Database, and then provide these dependencies to the Database service.
Avoiding Requirement Leakage
When constructing the Database service, it’s important to avoid exposing the dependencies on Config and Logger within the Database interface.
You might be tempted to define the Database service as follows:
Example (Leaking Dependencies in the Service Interface)
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.
@since ― 2.0.0
@since ― 2.0.0
Effect<unknown, never,
classConfig
Config|
classLogger
Logger>
17
}
18
>() {}
Here, the query function of the Database service requires both Config and Logger. This design leaks implementation details, making the Database service aware of its dependencies, which complicates testing and makes it difficult to mock.
To demonstrate the problem, let’s create a test instance of the Database service:
Example (Creating a Test Instance with Leaked Dependencies)
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.
@since ― 2.0.0
@since ― 2.0.0
Effect<unknown, never,
classConfig
Config|
classLogger
Logger>
16
}
17
>() {}
18
19
// Declaring a test instance of the Database service
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.
Tests for deep equality between the actual and expected parameters.
"Deep" equality means that the enumerable "own" properties of child objects
are recursively evaluated also by the following rules.
}) => <A, E, R>(self:Effect.Effect<A, E, R>) =>Effect.Effect<A, E, Exclude<R, Database>> (+1overload)
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
Because the Database service interface directly includes dependencies on Config and Logger, it forces any test setup to include these services, even if they’re irrelevant to the test. This adds unnecessary complexity and makes it difficult to write simple, isolated unit tests.
Instead of directly tying dependencies to the Database service interface, dependencies should be managed at the construction phase.
We can use layers to properly construct the Database service and manage its dependencies without leaking details into the interface.
Creating Layers
The Layer type is structured as follows:
┌─── The service to be created
│ ┌─── The possible error
│ │ ┌─── The required dependencies
▼ ▼ ▼
Layer<RequirementsOut, Error, RequirementsIn>
A Layer represents a blueprint for constructing a RequirementsOut (the service). It requires a RequirementsIn (dependencies) as input and may result in an error of type Error during the construction process.
Parameter
Description
RequirementsOut
The service or resource to be created.
Error
The type of error that might occur during the construction of the service.
RequirementsIn
The dependencies required to construct the service.
By using layers, you can better organize your services, ensuring that their dependencies are clearly defined and separated from their implementation details.
For simplicity, let’s assume that we won’t encounter any errors during the value construction (meaning Error = never).
Now, let’s determine how many layers we need to implement our dependency graph:
Layer
Dependencies
Type
ConfigLive
The Config service does not depend on any other services
Layer<Config>
LoggerLive
The Logger service depends on the Config service
Layer<Logger, never, Config>
DatabaseLive
The Database service depends on Config and Logger
Layer<Database, never, Config | Logger>
When a service has multiple dependencies, they are represented as a union type. In our case, the Database service depends on both the Config and Logger services. Therefore, the type for the DatabaseLive layer will be:
Layer<Database, never, Config | Logger>
Config
The Config service does not depend on any other services, so ConfigLive will be the simplest layer to implement. Just like in the Managing Services page, we must create a tag for the service. And because the service has no dependencies, we can create the layer directly using the Layer.succeed constructor:
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.
RequirementsOut is Config, indicating that constructing the layer will produce a Config service
Error is never, indicating that layer construction cannot fail
RequirementsIn is never, indicating that the layer has no dependencies
Note that, to construct ConfigLive, we used the Config.of
constructor. However, this is merely a helper to ensure correct type inference
for the implementation. It’s possible to skip this helper and construct the
implementation directly as a simple object:
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.
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.
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.
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.
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.
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(newError('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
constname='Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
constout=getStreamSomehow();
consterr=getStreamSomehow();
constmyConsole=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(newError('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
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()).
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.
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.
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.
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.
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(newError('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
constname='Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
constout=getStreamSomehow();
consterr=getStreamSomehow();
constmyConsole=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(newError('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
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()).
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.
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.
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.
Merges this layer with the specified layer concurrently, producing a new layer with combined input and output types.
@since ― 2.0.0
merge(
constlayer1:Layer.Layer<"Out1", never, "In1">
layer1,
constlayer2:Layer.Layer<"Out2", never, "In2">
layer2)
When we merge two layers, the resulting layer:
requires all the services that both of them require ("In1" | "In2").
produces all services that both of them produce ("Out1" | "Out2").
For example, in our web application above, we can merge our ConfigLive and LoggerLive layers into a single AppConfigLive layer, which retains the requirements of both layers (never | Config = Config) and the outputs of both layers (Config | Logger):
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.
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.
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.
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.
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(newError('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
constname='Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
constout=getStreamSomehow();
consterr=getStreamSomehow();
constmyConsole=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(newError('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
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()).
Feeds the output services of this builder into the input of the specified
builder, resulting in a new builder with the inputs of this builder as
well as any leftover inputs, and the outputs of the specified builder.
Sequential composition of layers implies that the output of one layer is supplied as the input for the inner layer,
resulting in a single layer with the requirements of the outer layer and the output of the inner.
Now we can compose the AppConfigLive layer with the DatabaseLive layer:
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.
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.
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.
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.
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(newError('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
constname='Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
constout=getStreamSomehow();
consterr=getStreamSomehow();
constmyConsole=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(newError('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
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()).
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.
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.
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.
Feeds the output services of this builder into the input of the specified
builder, resulting in a new builder with the inputs of this builder as
well as any leftover inputs, and the outputs of the specified builder.
Feeds the output services of this builder into the input of the specified
builder, resulting in a new builder with the inputs of this builder as
well as any leftover inputs, and the outputs of the specified builder.
@since ― 2.0.0
provide(
constConfigLive:Layer.Layer<Config, never, never>
ConfigLive)
75
)
We obtained a MainLive layer that produces the Database service:
Layer<Database, never, never>
This layer is the fully resolved layer for our application.
Merging and Composing Layers
Let’s say we want our MainLive layer to return both the Config and Database services. We can achieve this with Layer.provideMerge:
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.
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.
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.
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.
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(newError('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
constname='Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
constout=getStreamSomehow();
consterr=getStreamSomehow();
constmyConsole=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(newError('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
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()).
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.
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.
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.
Feeds the output services of this builder into the input of the specified
builder, resulting in a new builder with the inputs of this builder as
well as any leftover inputs, and the outputs of the specified builder.
Feeds the output services of this layer into the input of the specified
layer, resulting in a new layer with the inputs of this layer, and the
outputs of both layers.
@since ― 2.0.0
provideMerge(
constConfigLive:Layer.Layer<Config, never, never>
ConfigLive)
70
)
Providing a Layer to an Effect
Now that we have assembled the fully resolved MainLive for our application,
we can provide it to our program to satisfy the program’s requirements using Effect.provide:
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.
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.
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.
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.
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(newError('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
constname='Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
constout=getStreamSomehow();
consterr=getStreamSomehow();
constmyConsole=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(newError('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
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()).
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.
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.
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.
Feeds the output services of this builder into the input of the specified
builder, resulting in a new builder with the inputs of this builder as
well as any leftover inputs, and the outputs of the specified builder.
Feeds the output services of this builder into the input of the specified
builder, resulting in a new builder with the inputs of this builder as
well as any leftover inputs, and the outputs of the specified builder.
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.
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.
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)
Attaches callbacks for the resolution and/or rejection of the Promise.
@param ― onfulfilled The callback to execute when the Promise is resolved.
@param ― onrejected The callback to execute when the Promise is rejected.
@returns ― A Promise for the completion of which ever callback is executed.
then(
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(newError('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
constname='Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
constout=getStreamSomehow();
consterr=getStreamSomehow();
constmyConsole=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(newError('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
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()).
result: 'Results from mysql://username:password@hostname:port/database_name'
85
}
86
*/
Note that the runnable requirements type is never, indicating that the program does not require any additional services to run.
Converting a Layer to an Effect
Sometimes your entire application might be a Layer, for example, an HTTP server. You can convert that layer to an effect with Layer.launch. It constructs the layer and keeps it alive until interrupted.
Runs an effect in the background, returning a fiber that can be observed or
interrupted.
Unless you specifically need a Promise or synchronous operation, runFork
is a good default choice.
Details
This function is the foundational way to execute an effect in the background.
It creates a "fiber," a lightweight, cooperative thread of execution that can
be observed (to access its result), interrupted, or joined. Fibers are useful
for concurrent programming and allow effects to run independently of the main
program flow.
Once the effect is running in a fiber, you can monitor its progress, cancel
it if necessary, or retrieve its result when it completes. If the effect
fails, the fiber will propagate the failure, which you can observe and
handle.
When to Use
Use this function when you need to run an effect in the background,
especially if the effect is long-running or performs periodic tasks. It's
suitable for tasks that need to run independently but might still need
observation or management, like logging, monitoring, or scheduled tasks.
This function is ideal if you don't need the result immediately or if the
effect is part of a larger concurrent workflow.
Builds this layer and uses it until it is interrupted. This is useful when
your entire application is a layer, such as an HTTP server.
@since ― 2.0.0
launch(
constserver:Layer.Layer<HTTPServer, never, never>
server))
14
/*
15
Output:
16
Listening on http://localhost:3000
17
...
18
*/
Tapping
The Layer.tap and Layer.tapError functions allow you to perform additional effects based on the success or failure of a layer. These operations do not modify the layer’s signature but are useful for logging or performing side effects during layer construction.
Layer.tap: Executes a specified effect when the layer is successfully acquired.
Layer.tapError: Executes a specified effect when the layer fails to acquire.
Example (Logging Success and Failure During Layer Acquisition)
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.
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(newError('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
constname='Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
constout=getStreamSomehow();
consterr=getStreamSomehow();
constmyConsole=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(newError('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
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()).
Runs an effect in the background, returning a fiber that can be observed or
interrupted.
Unless you specifically need a Promise or synchronous operation, runFork
is a good default choice.
Details
This function is the foundational way to execute an effect in the background.
It creates a "fiber," a lightweight, cooperative thread of execution that can
be observed (to access its result), interrupted, or joined. Fibers are useful
for concurrent programming and allow effects to run independently of the main
program flow.
Once the effect is running in a fiber, you can monitor its progress, cancel
it if necessary, or retrieve its result when it completes. If the effect
fails, the fiber will propagate the failure, which you can observe and
handle.
When to Use
Use this function when you need to run an effect in the background,
especially if the effect is long-running or performs periodic tasks. It's
suitable for tasks that need to run independently but might still need
observation or management, like logging, monitoring, or scheduled tasks.
This function is ideal if you don't need the result immediately or if the
effect is part of a larger concurrent workflow.
(Missing data at HOST: "Expected HOST to exist in the process context")
28
*/
Error Handling
When constructing layers, it is important to handle potential errors. The Effect library provides tools like Layer.catchAll and Layer.orElse to manage errors and define fallback layers in case of failure.
catchAll
The Layer.catchAll function allows you to recover from errors during layer construction by specifying a fallback layer. This can be useful for handling specific error cases and ensuring the application can continue with an alternative setup.
Example (Recovering from Errors During Layer Construction)
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.
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(newError('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
constname='Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
constout=getStreamSomehow();
consterr=getStreamSomehow();
constmyConsole=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(newError('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
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()).
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.
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(newError('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
constname='Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
constout=getStreamSomehow();
consterr=getStreamSomehow();
constmyConsole=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(newError('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
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()).
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(newError('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
constname='Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
constout=getStreamSomehow();
consterr=getStreamSomehow();
constmyConsole=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(newError('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
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()).
Runs an effect in the background, returning a fiber that can be observed or
interrupted.
Unless you specifically need a Promise or synchronous operation, runFork
is a good default choice.
Details
This function is the foundational way to execute an effect in the background.
It creates a "fiber," a lightweight, cooperative thread of execution that can
be observed (to access its result), interrupted, or joined. Fibers are useful
for concurrent programming and allow effects to run independently of the main
program flow.
Once the effect is running in a fiber, you can monitor its progress, cancel
it if necessary, or retrieve its result when it completes. If the effect
fails, the fiber will propagate the failure, which you can observe and
handle.
When to Use
Use this function when you need to run an effect in the background,
especially if the effect is long-running or performs periodic tasks. It's
suitable for tasks that need to run independently but might still need
observation or management, like logging, monitoring, or scheduled tasks.
This function is ideal if you don't need the result immediately or if the
effect is part of a larger concurrent workflow.
Builds this layer and uses it until it is interrupted. This is useful when
your entire application is a layer, such as an HTTP server.
@since ― 2.0.0
launch(
constserver:Layer.Layer<HTTPServer, never, never>
server))
26
/*
27
Output:
28
Recovering from error:
29
(Missing data at HOST: "Expected HOST to exist in the process context")
30
Listening on http://localhost:3000
31
...
32
*/
orElse
The Layer.orElse function provides a simpler way to fall back to an alternative layer if the initial layer fails. Unlike Layer.catchAll, it does not receive the error as input. Use this when you only need to provide a default layer without reacting to specific errors.
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.
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(newError('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
constname='Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
constout=getStreamSomehow();
consterr=getStreamSomehow();
constmyConsole=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(newError('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
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()).
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.
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(newError('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
constname='Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
constout=getStreamSomehow();
consterr=getStreamSomehow();
constmyConsole=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(newError('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
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()).
Runs an effect in the background, returning a fiber that can be observed or
interrupted.
Unless you specifically need a Promise or synchronous operation, runFork
is a good default choice.
Details
This function is the foundational way to execute an effect in the background.
It creates a "fiber," a lightweight, cooperative thread of execution that can
be observed (to access its result), interrupted, or joined. Fibers are useful
for concurrent programming and allow effects to run independently of the main
program flow.
Once the effect is running in a fiber, you can monitor its progress, cancel
it if necessary, or retrieve its result when it completes. If the effect
fails, the fiber will propagate the failure, which you can observe and
handle.
When to Use
Use this function when you need to run an effect in the background,
especially if the effect is long-running or performs periodic tasks. It's
suitable for tasks that need to run independently but might still need
observation or management, like logging, monitoring, or scheduled tasks.
This function is ideal if you don't need the result immediately or if the
effect is part of a larger concurrent workflow.
Simplifying Service Definitions with Effect.Service
The Effect.Service API provides a way to define a service in a single step, including its tag and layer.
It also allows specifying dependencies upfront, making service construction more straightforward.
Defining a Service with Dependencies
The following example defines a Cache service that depends on a file system.
Simplifies the creation and management of services in Effect by defining both
a Tag and a Layer.
Details
This function allows you to streamline the creation of services by combining
the definition of a Context.Tag and a Layer in a single step. It supports
various ways of providing the service implementation:
Using an effect to define the service dynamically.
Using sync or succeed to define the service statically.
Using scoped to create services with lifecycle management.
It also allows you to specify dependencies for the service, which will be
provided automatically when the service is used. Accessors can be optionally
generated for the service, making it more convenient to use.
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.
Simplifies the creation and management of services in Effect by defining both
a Tag and a Layer.
Details
This function allows you to streamline the creation of services by combining
the definition of a Context.Tag and a Layer in a single step. It supports
various ways of providing the service implementation:
Using an effect to define the service dynamically.
Using sync or succeed to define the service statically.
Using scoped to create services with lifecycle management.
It also allows you to specify dependencies for the service, which will be
provided automatically when the service is used. Accessors can be optionally
generated for the service, making it more convenient to use.
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.
Simplifies the creation and management of services in Effect by defining both
a Tag and a Layer.
Details
This function allows you to streamline the creation of services by combining
the definition of a Context.Tag and a Layer in a single step. It supports
various ways of providing the service implementation:
Using an effect to define the service dynamically.
Using sync or succeed to define the service statically.
Using scoped to create services with lifecycle management.
It also allows you to specify dependencies for the service, which will be
provided automatically when the service is used. Accessors can be optionally
generated for the service, making it more convenient to use.
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.
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.
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(newError('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
constname='Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
constout=getStreamSomehow();
consterr=getStreamSomehow();
constmyConsole=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(newError('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
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()).
Handles both recoverable and unrecoverable errors by providing a recovery
effect.
When to Use
The catchAllCause function allows you to handle all errors, including
unrecoverable defects, by providing a recovery effect. The recovery logic is
based on the Cause of the error, which provides detailed information about
the failure.
When to Recover from Defects
Defects are unexpected errors that typically shouldn't be recovered from, as
they often indicate serious issues. However, in some cases, such as
dynamically loaded plugins, controlled recovery might be needed.
Example (Recovering from All Errors)
import { Cause, Effect } from"effect"
// Define an effect that may fail with a recoverable or unrecoverable error
constprogram= Effect.fail("Something went wrong!")
// Recover from all errors by examining the cause
constrecovered= program.pipe(
Effect.catchAllCause((cause) =>
Cause.isFailure(cause)
? Effect.succeed("Recovered from a regular error")
constprovide: <Cache, never, never>(layer:Layer<Cache, never, never>) => <A, E, R>(self:Effect.Effect<A, E, R>) =>Effect.Effect<A, E, Exclude<R, Cache>> (+9overloads)
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.
Runs an effect in the background, returning a fiber that can be observed or
interrupted.
Unless you specifically need a Promise or synchronous operation, runFork
is a good default choice.
Details
This function is the foundational way to execute an effect in the background.
It creates a "fiber," a lightweight, cooperative thread of execution that can
be observed (to access its result), interrupted, or joined. Fibers are useful
for concurrent programming and allow effects to run independently of the main
program flow.
Once the effect is running in a fiber, you can monitor its progress, cancel
it if necessary, or retrieve its result when it completes. If the effect
fails, the fiber will propagate the failure, which you can observe and
handle.
When to Use
Use this function when you need to run an effect in the background,
especially if the effect is long-running or performs periodic tasks. It's
suitable for tasks that need to run independently but might still need
observation or management, like logging, monitoring, or scheduled tasks.
This function is ideal if you don't need the result immediately or if the
effect is part of a larger concurrent workflow.
Simplifies the creation and management of services in Effect by defining both
a Tag and a Layer.
Details
This function allows you to streamline the creation of services by combining
the definition of a Context.Tag and a Layer in a single step. It supports
various ways of providing the service implementation:
Using an effect to define the service dynamically.
Using sync or succeed to define the service statically.
Using scoped to create services with lifecycle management.
It also allows you to specify dependencies for the service, which will be
provided automatically when the service is used. Accessors can be optionally
generated for the service, making it more convenient to use.
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.
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.
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(newError('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
constname='Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
constout=getStreamSomehow();
consterr=getStreamSomehow();
constmyConsole=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(newError('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
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()).
Handles both recoverable and unrecoverable errors by providing a recovery
effect.
When to Use
The catchAllCause function allows you to handle all errors, including
unrecoverable defects, by providing a recovery effect. The recovery logic is
based on the Cause of the error, which provides detailed information about
the failure.
When to Recover from Defects
Defects are unexpected errors that typically shouldn't be recovered from, as
they often indicate serious issues. However, in some cases, such as
dynamically loaded plugins, controlled recovery might be needed.
Example (Recovering from All Errors)
import { Cause, Effect } from"effect"
// Define an effect that may fail with a recoverable or unrecoverable error
constprogram= Effect.fail("Something went wrong!")
// Recover from all errors by examining the cause
constrecovered= program.pipe(
Effect.catchAllCause((cause) =>
Cause.isFailure(cause)
? Effect.succeed("Recovered from a regular error")
constprovide: <Cache, never, FileSystem.FileSystem>(layer:Layer<Cache, never, FileSystem.FileSystem>) => <A, E, R>(self:Effect.Effect<A, E, R>) =>Effect.Effect<A, E, FileSystem.FileSystem|Exclude<R, Cache>> (+9overloads)
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.
constprovide: <FileSystem.FileSystem, never, never>(layer:Layer<FileSystem.FileSystem, never, never>) => <A, E, R>(self:Effect.Effect<A, E, R>) =>Effect.Effect<A, E, Exclude<R, FileSystem.FileSystem>> (+9overloads)
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.
Runs an effect in the background, returning a fiber that can be observed or
interrupted.
Unless you specifically need a Promise or synchronous operation, runFork
is a good default choice.
Details
This function is the foundational way to execute an effect in the background.
It creates a "fiber," a lightweight, cooperative thread of execution that can
be observed (to access its result), interrupted, or joined. Fibers are useful
for concurrent programming and allow effects to run independently of the main
program flow.
Once the effect is running in a fiber, you can monitor its progress, cancel
it if necessary, or retrieve its result when it completes. If the effect
fails, the fiber will propagate the failure, which you can observe and
handle.
When to Use
Use this function when you need to run an effect in the background,
especially if the effect is long-running or performs periodic tasks. It's
suitable for tasks that need to run independently but might still need
observation or management, like logging, monitoring, or scheduled tasks.
This function is ideal if you don't need the result immediately or if the
effect is part of a larger concurrent workflow.
Simplifies the creation and management of services in Effect by defining both
a Tag and a Layer.
Details
This function allows you to streamline the creation of services by combining
the definition of a Context.Tag and a Layer in a single step. It supports
various ways of providing the service implementation:
Using an effect to define the service dynamically.
Using sync or succeed to define the service statically.
Using scoped to create services with lifecycle management.
It also allows you to specify dependencies for the service, which will be
provided automatically when the service is used. Accessors can be optionally
generated for the service, making it more convenient to use.
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.
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.
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(newError('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
constname='Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
constout=getStreamSomehow();
consterr=getStreamSomehow();
constmyConsole=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(newError('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
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()).
Handles both recoverable and unrecoverable errors by providing a recovery
effect.
When to Use
The catchAllCause function allows you to handle all errors, including
unrecoverable defects, by providing a recovery effect. The recovery logic is
based on the Cause of the error, which provides detailed information about
the failure.
When to Recover from Defects
Defects are unexpected errors that typically shouldn't be recovered from, as
they often indicate serious issues. However, in some cases, such as
dynamically loaded plugins, controlled recovery might be needed.
Example (Recovering from All Errors)
import { Cause, Effect } from"effect"
// Define an effect that may fail with a recoverable or unrecoverable error
constprogram= Effect.fail("Something went wrong!")
// Recover from all errors by examining the cause
constrecovered= program.pipe(
Effect.catchAllCause((cause) =>
Cause.isFailure(cause)
? Effect.succeed("Recovered from a regular error")
constprovideService: <Cache, Cache>(tag:Tag<Cache, Cache>, service:Cache) => <A, E, R>(self:Effect.Effect<A, E, R>) =>Effect.Effect<A, E, Exclude<R, Cache>> (+1overload)
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
Runs an effect in the background, returning a fiber that can be observed or
interrupted.
Unless you specifically need a Promise or synchronous operation, runFork
is a good default choice.
Details
This function is the foundational way to execute an effect in the background.
It creates a "fiber," a lightweight, cooperative thread of execution that can
be observed (to access its result), interrupted, or joined. Fibers are useful
for concurrent programming and allow effects to run independently of the main
program flow.
Once the effect is running in a fiber, you can monitor its progress, cancel
it if necessary, or retrieve its result when it completes. If the effect
fails, the fiber will propagate the failure, which you can observe and
handle.
When to Use
Use this function when you need to run an effect in the background,
especially if the effect is long-running or performs periodic tasks. It's
suitable for tasks that need to run independently but might still need
observation or management, like logging, monitoring, or scheduled tasks.
This function is ideal if you don't need the result immediately or if the
effect is part of a larger concurrent workflow.
Simplifies the creation and management of services in Effect by defining both
a Tag and a Layer.
Details
This function allows you to streamline the creation of services by combining
the definition of a Context.Tag and a Layer in a single step. It supports
various ways of providing the service implementation:
Using an effect to define the service dynamically.
Using sync or succeed to define the service statically.
Using scoped to create services with lifecycle management.
It also allows you to specify dependencies for the service, which will be
provided automatically when the service is used. Accessors can be optionally
generated for the service, making it more convenient to use.
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.
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(newError('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
constname='Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
constout=getStreamSomehow();
consterr=getStreamSomehow();
constmyConsole=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(newError('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
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()).
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)
constprovide: <Sync, never, never>(layer:Layer<Sync, never, never>) => <A, E, R>(self:Effect.Effect<A, E, R>) =>Effect.Effect<A, E, Exclude<R, Sync>> (+9overloads)
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.
Simplifies the creation and management of services in Effect by defining both
a Tag and a Layer.
Details
This function allows you to streamline the creation of services by combining
the definition of a Context.Tag and a Layer in a single step. It supports
various ways of providing the service implementation:
Using an effect to define the service dynamically.
Using sync or succeed to define the service statically.
Using scoped to create services with lifecycle management.
It also allows you to specify dependencies for the service, which will be
provided automatically when the service is used. Accessors can be optionally
generated for the service, making it more convenient to use.
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.
Creates a scoped resource using an acquire and release effect.
Details
This function helps manage resources by combining two Effect values: one
for acquiring the resource and one for releasing it.
acquireRelease does the following:
Ensures that the effect that acquires the resource will not be
interrupted. Note that acquisition may still fail due to internal
reasons (such as an uncaught exception).
Ensures that the release effect will not be interrupted, and will be
executed as long as the acquisition effect successfully acquires the
resource.
If the acquire function succeeds, the release function is added to the
list of finalizers for the scope. This ensures that the release will happen
automatically when the scope is closed.
Both acquire and release run uninterruptibly, meaning they cannot be
interrupted while they are executing.
Additionally, the release function can be influenced by the exit value when
the scope closes, allowing for custom handling of how the resource is
released based on the execution outcome.
When to Use
This function is used to ensure that an effect that represents the
acquisition of a resource (for example, opening a file, launching a thread,
etc.) will not be interrupted, and that the resource will always be released
when the Effect completes execution.
constas: <string>(value:string) => <A, E, R>(self:Effect.Effect<A, E, R>) =>Effect.Effect<string, E, R> (+1overload)
Replaces the value inside an effect with a constant value.
Details
This function allows you to ignore the original value inside an effect and
replace it with a constant value.
When to Use
It is useful when you no longer need the value produced by an effect but want
to ensure that the effect completes successfully with a specific constant
result instead. For instance, you can replace the value produced by a
computation with a predefined value, ignoring what was calculated before.
Example (Replacing a Value)
import { pipe, Effect } from"effect"
// Replaces the value 5 with the constant "new value"
Ensures a finalizer is added to the scope of the calling effect, guaranteeing
it runs when the scope is closed.
Details
This function adds a finalizer that will execute whenever the scope of the
effect is closed, regardless of whether the effect succeeds, fails, or is
interrupted. The finalizer receives the Exit value of the effect's scope,
allowing it to react differently depending on how the effect concludes.
Finalizers are a reliable way to manage resource cleanup, ensuring that
resources such as file handles, network connections, or database transactions
are properly closed even in the event of an unexpected interruption or error.
Finalizers operate in conjunction with Effect's scoped resources. If an
effect with a finalizer is wrapped in a scope, the finalizer will execute
automatically when the scope ends.
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.
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(newError('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
constname='Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
constout=getStreamSomehow();
consterr=getStreamSomehow();
constmyConsole=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(newError('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
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()).
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)
constprovide: <Scoped, never, never>(layer:Layer<Scoped, never, never>) => <A, E, R>(self:Effect.Effect<A, E, R>) =>Effect.Effect<A, E, Exclude<R, Scoped>> (+9overloads)
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.
Simplifies the creation and management of services in Effect by defining both
a Tag and a Layer.
Details
This function allows you to streamline the creation of services by combining
the definition of a Context.Tag and a Layer in a single step. It supports
various ways of providing the service implementation:
Using an effect to define the service dynamically.
Using sync or succeed to define the service statically.
Using scoped to create services with lifecycle management.
It also allows you to specify dependencies for the service, which will be
provided automatically when the service is used. Accessors can be optionally
generated for the service, making it more convenient to use.
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.
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(newError('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
constname='Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
constout=getStreamSomehow();
consterr=getStreamSomehow();
constmyConsole=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(newError('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
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()).
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)
constprovide: <Sync, never, never>(layer:Layer<Sync, never, never>) => <A, E, R>(self:Effect.Effect<A, E, R>) =>Effect.Effect<A, E, Exclude<R, Sync>> (+9overloads)
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.
// timestamp=... level=INFO fiber=#0 message="Executing query: SELECT * FROM users"
// []
@see ― provideService for providing a service to an effect.
@since ― 2.0.0
provide(
classSync
Sync.
typeDefault: Layer<Sync, never, never>
Default)))
18
// Example Output: The number is 3858843290019673
Effect.Service vs Context.Tag
Both Effect.Service and Context.Tag are ways to model services in the Effect ecosystem. They serve similar purposes but target different use-cases.
Feature
Effect.Service
Context.Tag
Tag creation
Generated for you (the class name acts as the tag)
You declare the tag manually
Default implementation
Required - supplied inline (effect, sync, etc.)
Optional - can be supplied later
Ready-made layers (.Default, …)
Automatically generated
You build layers yourself
Best suited for
Application code with a clear runtime implementation
Library code or dynamically-scoped values
When no sensible default exists
Not ideal; you would still have to invent one
Preferred
Key points
Less boilerplate:Effect.Service is syntactic sugar over Context.Tag plus the accompanying layer and helpers.
Default required:
A class that extends Effect.Service must declare one of the built-in constructors (effect, sync, succeed, or scoped). This baseline implementation becomes part of MyService.Default, so any code that imports the service can run without providing extra layers.
That is handy for app-level services where a sensible runtime implementation exists (logging, HTTP clients, real databases, and so on).
If your service is inherently contextual (for example, a per-request database handle) or you are writing a library that should not assume an implementation, prefer Context.Tag: you publish only the tag and let callers supply the layer that makes sense in their environment.
The class is the tag: When you create a class with extends Effect.Service, the class constructor itself acts as the tag. You can provide alternate implementations by supplying a value for that class when wiring layers: