Skip to content

Patterns

In certain scenarios, you might need to perform a sequence of chained operations where the success of each operation depends on the previous one. However, if any of the operations fail, you would want to reverse the effects of all previous successful operations. This pattern is valuable when you need to ensure that either all operations succeed, or none of them have any effect at all.

Effect offers a way to achieve this pattern using the Effect.acquireRelease function in combination with the Exit type. The Effect.acquireRelease function allows you to acquire a resource, perform operations with it, and release the resource when you’re done. The Exit type represents the outcome of an effectful computation, indicating whether it succeeded or failed.

Let’s go through an example of implementing this pattern. Suppose we want to create a “Workspace” in our application, which involves creating an S3 bucket, an ElasticSearch index, and a Database entry that relies on the previous two.

To begin, we define the domain model for the required services:

  • S3
  • ElasticSearch
  • Database
import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Context

@since2.0.0

@since2.0.0

Context
} from "effect"
class
class S3Error
S3Error
{
readonly
S3Error._tag: "S3Error"
_tag
= "S3Error"
}
interface
interface Bucket
Bucket
{
readonly
Bucket.name: string
name
: string
}
class
class S3
S3
extends
import Context

@since2.0.0

@since2.0.0

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

@example

import { Context, Layer } from "effect"

class MyTag extends Context.Tag("MyTag")< MyTag, { readonly myNum: number }

() { static Live = Layer.succeed(this, { myNum: 108 }) }

@since2.0.0

Tag
("S3")<
class S3
S3
,
{
readonly
createBucket: Effect.Effect<Bucket, S3Error, never>
createBucket
:
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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

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

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

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

@since2.0.0

@since2.0.0

Effect
<
interface Bucket
Bucket
,
class S3Error
S3Error
>
readonly
deleteBucket: (bucket: Bucket) => Effect.Effect<void>
deleteBucket
: (
bucket: Bucket
bucket
:
interface Bucket
Bucket
) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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

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

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

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

@since2.0.0

@since2.0.0

Effect
<void>
}
>() {}
class
class ElasticSearchError
ElasticSearchError
{
readonly
ElasticSearchError._tag: "ElasticSearchError"
_tag
= "ElasticSearchError"
}
interface
interface Index
Index
{
readonly
Index.id: string
id
: string
}
class
class ElasticSearch
ElasticSearch
extends
import Context

@since2.0.0

@since2.0.0

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

@example

import { Context, Layer } from "effect"

class MyTag extends Context.Tag("MyTag")< MyTag, { readonly myNum: number }

() { static Live = Layer.succeed(this, { myNum: 108 }) }

@since2.0.0

Tag
("ElasticSearch")<
class ElasticSearch
ElasticSearch
,
{
readonly
createIndex: Effect.Effect<Index, ElasticSearchError, never>
createIndex
:
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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

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

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

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

@since2.0.0

@since2.0.0

Effect
<
interface Index
Index
,
class ElasticSearchError
ElasticSearchError
>
readonly
deleteIndex: (index: Index) => Effect.Effect<void>
deleteIndex
: (
index: Index
index
:
interface Index
Index
) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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

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

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

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

@since2.0.0

@since2.0.0

Effect
<void>
}
>() {}
class
class DatabaseError
DatabaseError
{
readonly
DatabaseError._tag: "DatabaseError"
_tag
= "DatabaseError"
}
interface
interface Entry
Entry
{
readonly
Entry.id: string
id
: string
}
class
class Database
Database
extends
import Context

@since2.0.0

@since2.0.0

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

@example

import { Context, Layer } from "effect"

class MyTag extends Context.Tag("MyTag")< MyTag, { readonly myNum: number }

() { static Live = Layer.succeed(this, { myNum: 108 }) }

@since2.0.0

Tag
("Database")<
class Database
Database
,
{
readonly
createEntry: (bucket: Bucket, index: Index) => Effect.Effect<Entry, DatabaseError>
createEntry
: (
bucket: Bucket
bucket
:
interface Bucket
Bucket
,
index: Index
index
:
interface Index
Index
) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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

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

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

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

@since2.0.0

@since2.0.0

Effect
<
interface Entry
Entry
,
class DatabaseError
DatabaseError
>
readonly
deleteEntry: (entry: Entry) => Effect.Effect<void>
deleteEntry
: (
entry: Entry
entry
:
interface Entry
Entry
) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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

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

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

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

@since2.0.0

@since2.0.0

Effect
<void>
}
>() {}

Next, we define the three create actions and the overall transaction (make) for the

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Context

@since2.0.0

@since2.0.0

Context
,
import Exit
Exit
} from "effect"
50 collapsed lines
class
class S3Error
S3Error
{
readonly
S3Error._tag: "S3Error"
_tag
= "S3Error"
}
interface
interface Bucket
Bucket
{
readonly
Bucket.name: string
name
: string
}
class
class S3
S3
extends
import Context

@since2.0.0

@since2.0.0

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

@example

import { Context, Layer } from "effect"

class MyTag extends Context.Tag("MyTag")< MyTag, { readonly myNum: number }

() { static Live = Layer.succeed(this, { myNum: 108 }) }

@since2.0.0

Tag
("S3")<
class S3
S3
,
{
readonly
createBucket: Effect.Effect<Bucket, S3Error, never>
createBucket
:
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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

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

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

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

@since2.0.0

@since2.0.0

Effect
<
interface Bucket
Bucket
,
class S3Error
S3Error
>
readonly
deleteBucket: (bucket: Bucket) => Effect.Effect<void>
deleteBucket
: (
bucket: Bucket
bucket
:
interface Bucket
Bucket
) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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

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

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

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

@since2.0.0

@since2.0.0

Effect
<void>
}
>() {}
class
class ElasticSearchError
ElasticSearchError
{
readonly
ElasticSearchError._tag: "ElasticSearchError"
_tag
= "ElasticSearchError"
}
interface
interface Index
Index
{
readonly
Index.id: string
id
: string
}
class
class ElasticSearch
ElasticSearch
extends
import Context

@since2.0.0

@since2.0.0

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

@example

import { Context, Layer } from "effect"

class MyTag extends Context.Tag("MyTag")< MyTag, { readonly myNum: number }

() { static Live = Layer.succeed(this, { myNum: 108 }) }

@since2.0.0

Tag
("ElasticSearch")<
class ElasticSearch
ElasticSearch
,
{
readonly
createIndex: Effect.Effect<Index, ElasticSearchError, never>
createIndex
:
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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

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

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

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

@since2.0.0

@since2.0.0

Effect
<
interface Index
Index
,
class ElasticSearchError
ElasticSearchError
>
readonly
deleteIndex: (index: Index) => Effect.Effect<void>
deleteIndex
: (
index: Index
index
:
interface Index
Index
) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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

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

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

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

@since2.0.0

@since2.0.0

Effect
<void>
}
>() {}
class
class DatabaseError
DatabaseError
{
readonly
DatabaseError._tag: "DatabaseError"
_tag
= "DatabaseError"
}
interface
interface Entry
Entry
{
readonly
Entry.id: string
id
: string
}
class
class Database
Database
extends
import Context

@since2.0.0

@since2.0.0

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

@example

import { Context, Layer } from "effect"

class MyTag extends Context.Tag("MyTag")< MyTag, { readonly myNum: number }

() { static Live = Layer.succeed(this, { myNum: 108 }) }

@since2.0.0

Tag
("Database")<
class Database
Database
,
{
readonly
createEntry: (bucket: Bucket, index: Index) => Effect.Effect<Entry, DatabaseError>
createEntry
: (
bucket: Bucket
bucket
:
interface Bucket
Bucket
,
index: Index
index
:
interface Index
Index
) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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

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

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

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

@since2.0.0

@since2.0.0

Effect
<
interface Entry
Entry
,
class DatabaseError
DatabaseError
>
readonly
deleteEntry: (entry: Entry) => Effect.Effect<void>
deleteEntry
: (
entry: Entry
entry
:
interface Entry
Entry
) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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

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

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

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

@since2.0.0

@since2.0.0

Effect
<void>
}
>() {}
// Create a bucket, and define the release function that deletes the
// bucket if the operation fails.
const
const createBucket: Effect.Effect<Bucket, S3Error, S3 | Scope>
createBucket
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Context.Tag<S3, {
readonly createBucket: Effect.Effect<Bucket, S3Error>;
readonly deleteBucket: (bucket: Bucket) => Effect.Effect<void>;
}>> | YieldWrap<...>, Bucket>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+1 overload)

@since2.0.0

gen
(function* () {
const {
const createBucket: Effect.Effect<Bucket, S3Error, never>
createBucket
,
const deleteBucket: (bucket: Bucket) => Effect.Effect<void>
deleteBucket
} = yield*
class S3
S3
return yield*
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const acquireRelease: <Bucket, S3Error, never, void, never>(acquire: Effect.Effect<Bucket, S3Error, never>, release: (a: Bucket, exit: Exit.Exit<unknown, unknown>) => Effect.Effect<...>) => Effect.Effect<...> (+1 overload)

This function constructs a scoped resource from an acquire and release Effect value.

If the acquire Effect value successfully completes execution, then the release Effect value will be added to the finalizers associated with the scope of this Effect value, and it is guaranteed to be run when the scope is closed.

The acquire and release Effect values will be run uninterruptibly. Additionally, the release Effect value may depend on the Exit value specified when the scope is closed.

@paramacquire - The Effect value that acquires the resource.

@paramrelease - The Effect value that releases the resource.

@returnsA new Effect value that represents the scoped resource.

@since2.0.0

acquireRelease
(
const createBucket: Effect.Effect<Bucket, S3Error, never>
createBucket
, (
bucket: Bucket
bucket
,
exit: Exit.Exit<unknown, unknown>
exit
) =>
// The release function for the Effect.acquireRelease operation is
// responsible for handling the acquired resource (bucket) after the
// main effect has completed. It is called regardless of whether the
// main effect succeeded or failed. If the main effect failed,
// Exit.isFailure(exit) will be true, and the function will perform
// a rollback by calling deleteBucket(bucket). If the main effect
// succeeded, Exit.isFailure(exit) will be false, and the function
// will return Effect.void, representing a successful, but
// do-nothing effect.
import Exit
Exit
.
const isFailure: <unknown, unknown>(self: Exit.Exit<unknown, unknown>) => self is Exit.Failure<unknown, unknown>

Returns true if the specified Exit is a Failure, false otherwise.

@since2.0.0

isFailure
(
exit: Exit.Exit<unknown, unknown>
exit
) ?
const deleteBucket: (bucket: Bucket) => Effect.Effect<void>
deleteBucket
(
bucket: Bucket
bucket
) :
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const void: Effect.Effect<void, never, never>
export void

@since2.0.0

void
)
})
// Create an index, and define the release function that deletes the
// index if the operation fails.
const
const createIndex: Effect.Effect<Index, ElasticSearchError, ElasticSearch | Scope>
createIndex
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Context.Tag<ElasticSearch, {
readonly createIndex: Effect.Effect<Index, ElasticSearchError>;
readonly deleteIndex: (index: Index) => Effect.Effect<void>;
}>> | YieldWrap<...>, Index>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+1 overload)

@since2.0.0

gen
(function* () {
const {
const createIndex: Effect.Effect<Index, ElasticSearchError, never>
createIndex
,
const deleteIndex: (index: Index) => Effect.Effect<void>
deleteIndex
} = yield*
class ElasticSearch
ElasticSearch
return yield*
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const acquireRelease: <Index, ElasticSearchError, never, void, never>(acquire: Effect.Effect<Index, ElasticSearchError, never>, release: (a: Index, exit: Exit.Exit<unknown, unknown>) => Effect.Effect<...>) => Effect.Effect<...> (+1 overload)

This function constructs a scoped resource from an acquire and release Effect value.

If the acquire Effect value successfully completes execution, then the release Effect value will be added to the finalizers associated with the scope of this Effect value, and it is guaranteed to be run when the scope is closed.

The acquire and release Effect values will be run uninterruptibly. Additionally, the release Effect value may depend on the Exit value specified when the scope is closed.

@paramacquire - The Effect value that acquires the resource.

@paramrelease - The Effect value that releases the resource.

@returnsA new Effect value that represents the scoped resource.

@since2.0.0

acquireRelease
(
const createIndex: Effect.Effect<Index, ElasticSearchError, never>
createIndex
, (
index: Index
index
,
exit: Exit.Exit<unknown, unknown>
exit
) =>
import Exit
Exit
.
const isFailure: <unknown, unknown>(self: Exit.Exit<unknown, unknown>) => self is Exit.Failure<unknown, unknown>

Returns true if the specified Exit is a Failure, false otherwise.

@since2.0.0

isFailure
(
exit: Exit.Exit<unknown, unknown>
exit
) ?
const deleteIndex: (index: Index) => Effect.Effect<void>
deleteIndex
(
index: Index
index
) :
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const void: Effect.Effect<void, never, never>
export void

@since2.0.0

void
)
})
// Create an entry in the database, and define the release function that
// deletes the entry if the operation fails.
const
const createEntry: (bucket: Bucket, index: Index) => Effect.Effect<Entry, DatabaseError, Database | Scope>
createEntry
= (
bucket: Bucket
bucket
:
interface Bucket
Bucket
,
index: Index
index
:
interface Index
Index
) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Context.Tag<Database, {
readonly createEntry: (bucket: Bucket, index: Index) => Effect.Effect<Entry, DatabaseError>;
readonly deleteEntry: (entry: Entry) => Effect.Effect<void>;
}>> | YieldWrap<...>, Entry>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+1 overload)

@since2.0.0

gen
(function* () {
const {
const createEntry: (bucket: Bucket, index: Index) => Effect.Effect<Entry, DatabaseError>
createEntry
,
const deleteEntry: (entry: Entry) => Effect.Effect<void>
deleteEntry
} = yield*
class Database
Database
return yield*
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const acquireRelease: <Entry, DatabaseError, never, void, never>(acquire: Effect.Effect<Entry, DatabaseError, never>, release: (a: Entry, exit: Exit.Exit<unknown, unknown>) => Effect.Effect<...>) => Effect.Effect<...> (+1 overload)

This function constructs a scoped resource from an acquire and release Effect value.

If the acquire Effect value successfully completes execution, then the release Effect value will be added to the finalizers associated with the scope of this Effect value, and it is guaranteed to be run when the scope is closed.

The acquire and release Effect values will be run uninterruptibly. Additionally, the release Effect value may depend on the Exit value specified when the scope is closed.

@paramacquire - The Effect value that acquires the resource.

@paramrelease - The Effect value that releases the resource.

@returnsA new Effect value that represents the scoped resource.

@since2.0.0

acquireRelease
(
const createEntry: (bucket: Bucket, index: Index) => Effect.Effect<Entry, DatabaseError>
createEntry
(
bucket: Bucket
bucket
,
index: Index
index
),
(
entry: Entry
entry
,
exit: Exit.Exit<unknown, unknown>
exit
) =>
import Exit
Exit
.
const isFailure: <unknown, unknown>(self: Exit.Exit<unknown, unknown>) => self is Exit.Failure<unknown, unknown>

Returns true if the specified Exit is a Failure, false otherwise.

@since2.0.0

isFailure
(
exit: Exit.Exit<unknown, unknown>
exit
) ?
const deleteEntry: (entry: Entry) => Effect.Effect<void>
deleteEntry
(
entry: Entry
entry
) :
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const void: Effect.Effect<void, never, never>
export void

@since2.0.0

void
)
})
const
const make: Effect.Effect<Entry, S3Error | ElasticSearchError | DatabaseError, S3 | ElasticSearch | Database>
make
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const scoped: <Entry, S3Error | ElasticSearchError | DatabaseError, S3 | ElasticSearch | Database | Scope>(effect: Effect.Effect<...>) => Effect.Effect<...>

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

@since2.0.0

scoped
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<Bucket, S3Error, S3 | Scope>> | YieldWrap<Effect.Effect<Index, ElasticSearchError, ElasticSearch | Scope>> | YieldWrap<...>, Entry>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+1 overload)

@since2.0.0

gen
(function* () {
const
const bucket: Bucket
bucket
= yield*
const createBucket: Effect.Effect<Bucket, S3Error, S3 | Scope>
createBucket
const
const index: Index
index
= yield*
const createIndex: Effect.Effect<Index, ElasticSearchError, ElasticSearch | Scope>
createIndex
return yield*
const createEntry: (bucket: Bucket, index: Index) => Effect.Effect<Entry, DatabaseError, Database | Scope>
createEntry
(
const bucket: Bucket
bucket
,
const index: Index
index
)
})
)

We then create simple service implementations to test the behavior of our Workspace code. To achieve this, we will utilize layers to construct test These layers will be able to handle various scenarios, including errors, which we can control using the FailureCase type.

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Context

@since2.0.0

@since2.0.0

Context
,
import Layer
Layer
,
import Console
Console
,
import Exit
Exit
} from "effect"
97 collapsed lines
class
class S3Error
S3Error
{
readonly
S3Error._tag: "S3Error"
_tag
= "S3Error"
}
interface
interface Bucket
Bucket
{
readonly
Bucket.name: string
name
: string
}
class
class S3
S3
extends
import Context

@since2.0.0

@since2.0.0

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

@example

import { Context, Layer } from "effect"

class MyTag extends Context.Tag("MyTag")< MyTag, { readonly myNum: number }

() { static Live = Layer.succeed(this, { myNum: 108 }) }

@since2.0.0

Tag
("S3")<
class S3
S3
,
{
readonly
createBucket: Effect.Effect<Bucket, S3Error, never>
createBucket
:
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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

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

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

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

@since2.0.0

@since2.0.0

Effect
<
interface Bucket
Bucket
,
class S3Error
S3Error
>
readonly
deleteBucket: (bucket: Bucket) => Effect.Effect<void>
deleteBucket
: (
bucket: Bucket
bucket
:
interface Bucket
Bucket
) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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

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

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

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

@since2.0.0

@since2.0.0

Effect
<void>
}
>() {}
class
class ElasticSearchError
ElasticSearchError
{
readonly
ElasticSearchError._tag: "ElasticSearchError"
_tag
= "ElasticSearchError"
}
interface
interface Index
Index
{
readonly
Index.id: string
id
: string
}
class
class ElasticSearch
ElasticSearch
extends
import Context

@since2.0.0

@since2.0.0

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

@example

import { Context, Layer } from "effect"

class MyTag extends Context.Tag("MyTag")< MyTag, { readonly myNum: number }

() { static Live = Layer.succeed(this, { myNum: 108 }) }

@since2.0.0

Tag
("ElasticSearch")<
class ElasticSearch
ElasticSearch
,
{
readonly
createIndex: Effect.Effect<Index, ElasticSearchError, never>
createIndex
:
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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

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

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

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

@since2.0.0

@since2.0.0

Effect
<
interface Index
Index
,
class ElasticSearchError
ElasticSearchError
>
readonly
deleteIndex: (index: Index) => Effect.Effect<void>
deleteIndex
: (
index: Index
index
:
interface Index
Index
) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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

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

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

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

@since2.0.0

@since2.0.0

Effect
<void>
}
>() {}
class
class DatabaseError
DatabaseError
{
readonly
DatabaseError._tag: "DatabaseError"
_tag
= "DatabaseError"
}
interface
interface Entry
Entry
{
readonly
Entry.id: string
id
: string
}
class
class Database
Database
extends
import Context

@since2.0.0

@since2.0.0

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

@example

import { Context, Layer } from "effect"

class MyTag extends Context.Tag("MyTag")< MyTag, { readonly myNum: number }

() { static Live = Layer.succeed(this, { myNum: 108 }) }

@since2.0.0

Tag
("Database")<
class Database
Database
,
{
readonly
createEntry: (bucket: Bucket, index: Index) => Effect.Effect<Entry, DatabaseError>
createEntry
: (
bucket: Bucket
bucket
:
interface Bucket
Bucket
,
index: Index
index
:
interface Index
Index
) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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

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

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

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

@since2.0.0

@since2.0.0

Effect
<
interface Entry
Entry
,
class DatabaseError
DatabaseError
>
readonly
deleteEntry: (entry: Entry) => Effect.Effect<void>
deleteEntry
: (
entry: Entry
entry
:
interface Entry
Entry
) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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

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

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

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

@since2.0.0

@since2.0.0

Effect
<void>
}
>() {}
// Create a bucket, and define the release function that deletes the
// bucket if the operation fails.
const
const createBucket: Effect.Effect<Bucket, S3Error, S3 | Scope>
createBucket
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Context.Tag<S3, {
readonly createBucket: Effect.Effect<Bucket, S3Error>;
readonly deleteBucket: (bucket: Bucket) => Effect.Effect<void>;
}>> | YieldWrap<...>, Bucket>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+1 overload)

@since2.0.0

gen
(function* () {
const {
const createBucket: Effect.Effect<Bucket, S3Error, never>
createBucket
,
const deleteBucket: (bucket: Bucket) => Effect.Effect<void>
deleteBucket
} = yield*
class S3
S3
return yield*
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const acquireRelease: <Bucket, S3Error, never, void, never>(acquire: Effect.Effect<Bucket, S3Error, never>, release: (a: Bucket, exit: Exit.Exit<unknown, unknown>) => Effect.Effect<...>) => Effect.Effect<...> (+1 overload)

This function constructs a scoped resource from an acquire and release Effect value.

If the acquire Effect value successfully completes execution, then the release Effect value will be added to the finalizers associated with the scope of this Effect value, and it is guaranteed to be run when the scope is closed.

The acquire and release Effect values will be run uninterruptibly. Additionally, the release Effect value may depend on the Exit value specified when the scope is closed.

@paramacquire - The Effect value that acquires the resource.

@paramrelease - The Effect value that releases the resource.

@returnsA new Effect value that represents the scoped resource.

@since2.0.0

acquireRelease
(
const createBucket: Effect.Effect<Bucket, S3Error, never>
createBucket
, (
bucket: Bucket
bucket
,
exit: Exit.Exit<unknown, unknown>
exit
) =>
// The release function for the Effect.acquireRelease operation is
// responsible for handling the acquired resource (bucket) after the
// main effect has completed. It is called regardless of whether the
// main effect succeeded or failed. If the main effect failed,
// Exit.isFailure(exit) will be true, and the function will perform
// a rollback by calling deleteBucket(bucket). If the main effect
// succeeded, Exit.isFailure(exit) will be false, and the function
// will return Effect.void, representing a successful, but
// do-nothing effect.
import Exit
Exit
.
const isFailure: <unknown, unknown>(self: Exit.Exit<unknown, unknown>) => self is Exit.Failure<unknown, unknown>

Returns true if the specified Exit is a Failure, false otherwise.

@since2.0.0

isFailure
(
exit: Exit.Exit<unknown, unknown>
exit
) ?
const deleteBucket: (bucket: Bucket) => Effect.Effect<void>
deleteBucket
(
bucket: Bucket
bucket
) :
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const void: Effect.Effect<void, never, never>
export void

@since2.0.0

void
)
})
// Create an index, and define the release function that deletes the
// index if the operation fails.
const
const createIndex: Effect.Effect<Index, ElasticSearchError, ElasticSearch | Scope>
createIndex
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Context.Tag<ElasticSearch, {
readonly createIndex: Effect.Effect<Index, ElasticSearchError>;
readonly deleteIndex: (index: Index) => Effect.Effect<void>;
}>> | YieldWrap<...>, Index>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+1 overload)

@since2.0.0

gen
(function* () {
const {
const createIndex: Effect.Effect<Index, ElasticSearchError, never>
createIndex
,
const deleteIndex: (index: Index) => Effect.Effect<void>
deleteIndex
} = yield*
class ElasticSearch
ElasticSearch
return yield*
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const acquireRelease: <Index, ElasticSearchError, never, void, never>(acquire: Effect.Effect<Index, ElasticSearchError, never>, release: (a: Index, exit: Exit.Exit<unknown, unknown>) => Effect.Effect<...>) => Effect.Effect<...> (+1 overload)

This function constructs a scoped resource from an acquire and release Effect value.

If the acquire Effect value successfully completes execution, then the release Effect value will be added to the finalizers associated with the scope of this Effect value, and it is guaranteed to be run when the scope is closed.

The acquire and release Effect values will be run uninterruptibly. Additionally, the release Effect value may depend on the Exit value specified when the scope is closed.

@paramacquire - The Effect value that acquires the resource.

@paramrelease - The Effect value that releases the resource.

@returnsA new Effect value that represents the scoped resource.

@since2.0.0

acquireRelease
(
const createIndex: Effect.Effect<Index, ElasticSearchError, never>
createIndex
, (
index: Index
index
,
exit: Exit.Exit<unknown, unknown>
exit
) =>
import Exit
Exit
.
const isFailure: <unknown, unknown>(self: Exit.Exit<unknown, unknown>) => self is Exit.Failure<unknown, unknown>

Returns true if the specified Exit is a Failure, false otherwise.

@since2.0.0

isFailure
(
exit: Exit.Exit<unknown, unknown>
exit
) ?
const deleteIndex: (index: Index) => Effect.Effect<void>
deleteIndex
(
index: Index
index
) :
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const void: Effect.Effect<void, never, never>
export void

@since2.0.0

void
)
})
// Create an entry in the database, and define the release function that
// deletes the entry if the operation fails.
const
const createEntry: (bucket: Bucket, index: Index) => Effect.Effect<Entry, DatabaseError, Database | Scope>
createEntry
= (
bucket: Bucket
bucket
:
interface Bucket
Bucket
,
index: Index
index
:
interface Index
Index
) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Context.Tag<Database, {
readonly createEntry: (bucket: Bucket, index: Index) => Effect.Effect<Entry, DatabaseError>;
readonly deleteEntry: (entry: Entry) => Effect.Effect<void>;
}>> | YieldWrap<...>, Entry>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+1 overload)

@since2.0.0

gen
(function* () {
const {
const createEntry: (bucket: Bucket, index: Index) => Effect.Effect<Entry, DatabaseError>
createEntry
,
const deleteEntry: (entry: Entry) => Effect.Effect<void>
deleteEntry
} = yield*
class Database
Database
return yield*
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const acquireRelease: <Entry, DatabaseError, never, void, never>(acquire: Effect.Effect<Entry, DatabaseError, never>, release: (a: Entry, exit: Exit.Exit<unknown, unknown>) => Effect.Effect<...>) => Effect.Effect<...> (+1 overload)

This function constructs a scoped resource from an acquire and release Effect value.

If the acquire Effect value successfully completes execution, then the release Effect value will be added to the finalizers associated with the scope of this Effect value, and it is guaranteed to be run when the scope is closed.

The acquire and release Effect values will be run uninterruptibly. Additionally, the release Effect value may depend on the Exit value specified when the scope is closed.

@paramacquire - The Effect value that acquires the resource.

@paramrelease - The Effect value that releases the resource.

@returnsA new Effect value that represents the scoped resource.

@since2.0.0

acquireRelease
(
const createEntry: (bucket: Bucket, index: Index) => Effect.Effect<Entry, DatabaseError>
createEntry
(
bucket: Bucket
bucket
,
index: Index
index
),
(
entry: Entry
entry
,
exit: Exit.Exit<unknown, unknown>
exit
) =>
import Exit
Exit
.
const isFailure: <unknown, unknown>(self: Exit.Exit<unknown, unknown>) => self is Exit.Failure<unknown, unknown>

Returns true if the specified Exit is a Failure, false otherwise.

@since2.0.0

isFailure
(
exit: Exit.Exit<unknown, unknown>
exit
) ?
const deleteEntry: (entry: Entry) => Effect.Effect<void>
deleteEntry
(
entry: Entry
entry
) :
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const void: Effect.Effect<void, never, never>
export void

@since2.0.0

void
)
})
const
const make: Effect.Effect<Entry, S3Error | ElasticSearchError | DatabaseError, S3 | ElasticSearch | Database>
make
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const scoped: <Entry, S3Error | ElasticSearchError | DatabaseError, S3 | ElasticSearch | Database | Scope>(effect: Effect.Effect<...>) => Effect.Effect<...>

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

@since2.0.0

scoped
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<Bucket, S3Error, S3 | Scope>> | YieldWrap<Effect.Effect<Index, ElasticSearchError, ElasticSearch | Scope>> | YieldWrap<...>, Entry>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+1 overload)

@since2.0.0

gen
(function* () {
const
const bucket: Bucket
bucket
= yield*
const createBucket: Effect.Effect<Bucket, S3Error, S3 | Scope>
createBucket
const
const index: Index
index
= yield*
const createIndex: Effect.Effect<Index, ElasticSearchError, ElasticSearch | Scope>
createIndex
return yield*
const createEntry: (bucket: Bucket, index: Index) => Effect.Effect<Entry, DatabaseError, Database | Scope>
createEntry
(
const bucket: Bucket
bucket
,
const index: Index
index
)
})
)
// The `FailureCaseLiterals` type allows us to provide different error
// scenarios while testing our
//
// For example, by providing the value "S3", we can simulate an error
// scenario specific to the S3 service. This helps us ensure that our
// program handles errors correctly and behaves as expected in various
// situations.
//
// Similarly, we can provide other values like "ElasticSearch" or
// "Database" to simulate error scenarios for those In cases
// where we want to test the absence of errors, we can provide
// `undefined`. By using this parameter, we can thoroughly test our
// services and verify their behavior under different error conditions.
type
type FailureCaseLiterals = "S3" | "ElasticSearch" | "Database" | undefined
FailureCaseLiterals
= "S3" | "ElasticSearch" | "Database" | undefined
class
class FailureCase
FailureCase
extends
import Context

@since2.0.0

@since2.0.0

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

@example

import { Context, Layer } from "effect"

class MyTag extends Context.Tag("MyTag")< MyTag, { readonly myNum: number }

() { static Live = Layer.succeed(this, { myNum: 108 }) }

@since2.0.0

Tag
("FailureCase")<
class FailureCase
FailureCase
,
type FailureCaseLiterals = "S3" | "ElasticSearch" | "Database" | undefined
FailureCaseLiterals
>() {}
// Create a test layer for the S3 service
const
const S3Test: Layer.Layer<S3, never, FailureCase>
S3Test
=
import Layer
Layer
.
const effect: <typeof S3, never, FailureCase>(tag: typeof S3, effect: Effect.Effect<{
readonly createBucket: Effect.Effect<Bucket, S3Error>;
readonly deleteBucket: (bucket: Bucket) => Effect.Effect<void>;
}, never, FailureCase>) => Layer.Layer<...> (+1 overload)

Constructs a layer from the specified effect.

@since2.0.0

effect
(
class S3
S3
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Context.Tag<FailureCase, FailureCaseLiterals>>, {
createBucket: Effect.Effect<{
name: string;
}, S3Error, never>;
deleteBucket: (bucket: Bucket) => Effect.Effect<...>;
}>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+1 overload)

@since2.0.0

gen
(function* () {
const
const failureCase: FailureCaseLiterals
failureCase
= yield*
class FailureCase
FailureCase
return {
createBucket: Effect.Effect<{
name: string;
}, S3Error, never>
createBucket
:
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<never, S3Error, never>>, {
name: string;
}>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<never, S3Error, never>>, {
name: string;
}, never>) => Effect.Effect<...> (+1 overload)

@since2.0.0

gen
(function* () {
var console: Console

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

The module exports two specific components:

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

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

Example using the global console:

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

Example using the Console class:

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

@seesource

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

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

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

See util.format() for more information.

@sincev0.1.100

log
("[S3] creating bucket")
if (
const failureCase: FailureCaseLiterals
failureCase
=== "S3") {
return yield*
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const fail: <S3Error>(error: S3Error) => Effect.Effect<never, S3Error, never>

Creates an Effect that represents a recoverable error.

This Effect does not succeed but instead fails with the provided error. The failure can be of any type, and will propagate through the effect pipeline unless handled.

Use this function when you want to explicitly signal an error in an Effect computation. The failed effect can later be handled with functions like

catchAll

or

catchTag

.

@example

import { Effect } from "effect"

// Example of creating a failed effect const failedEffect = Effect.fail("Something went wrong")

// Handle the failure failedEffect.pipe( Effect.catchAll((error) => Effect.succeed(Recovered from: ${error})), Effect.runPromise ).then(console.log) // Output: "Recovered from: Something went wrong"

@since2.0.0

fail
(new
constructor S3Error(): S3Error
S3Error
())
} else {
return {
name: string
name
: "<bucket.name>" }
}
}),
deleteBucket: (bucket: Bucket) => Effect.Effect<void, never, never>
deleteBucket
: (
bucket: Bucket
bucket
) =>
import Console
Console
.
const log: (...args: ReadonlyArray<any>) => Effect.Effect<void>

@since2.0.0

log
(`[S3] delete bucket ${
bucket: Bucket
bucket
.
Bucket.name: string
name
}`)
}
})
)
// Create a test layer for the ElasticSearch service
const
const ElasticSearchTest: Layer.Layer<ElasticSearch, never, FailureCase>
ElasticSearchTest
=
import Layer
Layer
.
const effect: <typeof ElasticSearch, never, FailureCase>(tag: typeof ElasticSearch, effect: Effect.Effect<{
readonly createIndex: Effect.Effect<Index, ElasticSearchError>;
readonly deleteIndex: (index: Index) => Effect.Effect<void>;
}, never, FailureCase>) => Layer.Layer<...> (+1 overload)

Constructs a layer from the specified effect.

@since2.0.0

effect
(
class ElasticSearch
ElasticSearch
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Context.Tag<FailureCase, FailureCaseLiterals>>, {
createIndex: Effect.Effect<{
id: string;
}, ElasticSearchError, never>;
deleteIndex: (index: Index) => Effect.Effect<...>;
}>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+1 overload)

@since2.0.0

gen
(function* () {
const
const failureCase: FailureCaseLiterals
failureCase
= yield*
class FailureCase
FailureCase
return {
createIndex: Effect.Effect<{
id: string;
}, ElasticSearchError, never>
createIndex
:
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<never, ElasticSearchError, never>>, {
id: string;
}>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<never, ElasticSearchError, never>>, {
id: string;
}, never>) => Effect.Effect<...> (+1 overload)

@since2.0.0

gen
(function* () {
var console: Console

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

The module exports two specific components:

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

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

Example using the global console:

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

Example using the Console class:

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

@seesource

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

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

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

See util.format() for more information.

@sincev0.1.100

log
("[ElasticSearch] creating index")
if (
const failureCase: FailureCaseLiterals
failureCase
=== "ElasticSearch") {
return yield*
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const fail: <ElasticSearchError>(error: ElasticSearchError) => Effect.Effect<never, ElasticSearchError, never>

Creates an Effect that represents a recoverable error.

This Effect does not succeed but instead fails with the provided error. The failure can be of any type, and will propagate through the effect pipeline unless handled.

Use this function when you want to explicitly signal an error in an Effect computation. The failed effect can later be handled with functions like

catchAll

or

catchTag

.

@example

import { Effect } from "effect"

// Example of creating a failed effect const failedEffect = Effect.fail("Something went wrong")

// Handle the failure failedEffect.pipe( Effect.catchAll((error) => Effect.succeed(Recovered from: ${error})), Effect.runPromise ).then(console.log) // Output: "Recovered from: Something went wrong"

@since2.0.0

fail
(new
constructor ElasticSearchError(): ElasticSearchError
ElasticSearchError
())
} else {
return {
id: string
id
: "<index.id>" }
}
}),
deleteIndex: (index: Index) => Effect.Effect<void, never, never>
deleteIndex
: (
index: Index
index
) =>
import Console
Console
.
const log: (...args: ReadonlyArray<any>) => Effect.Effect<void>

@since2.0.0

log
(`[ElasticSearch] delete index ${
index: Index
index
.
Index.id: string
id
}`)
}
})
)
// Create a test layer for the Database service
const
const DatabaseTest: Layer.Layer<Database, never, FailureCase>
DatabaseTest
=
import Layer
Layer
.
const effect: <typeof Database, never, FailureCase>(tag: typeof Database, effect: Effect.Effect<{
readonly createEntry: (bucket: Bucket, index: Index) => Effect.Effect<Entry, DatabaseError>;
readonly deleteEntry: (entry: Entry) => Effect.Effect<void>;
}, never, FailureCase>) => Layer.Layer<...> (+1 overload)

Constructs a layer from the specified effect.

@since2.0.0

effect
(
class Database
Database
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Context.Tag<FailureCase, FailureCaseLiterals>>, {
createEntry: (bucket: Bucket, index: Index) => Effect.Effect<...>;
deleteEntry: (entry: Entry) => Effect.Effect<...>;
}>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+1 overload)

@since2.0.0

gen
(function* () {
const
const failureCase: FailureCaseLiterals
failureCase
= yield*
class FailureCase
FailureCase
return {
createEntry: (bucket: Bucket, index: Index) => Effect.Effect<{
id: string;
}, DatabaseError, never>
createEntry
: (
bucket: Bucket
bucket
,
index: Index
index
) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<never, DatabaseError, never>>, {
id: string;
}>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<never, DatabaseError, never>>, {
id: string;
}, never>) => Effect.Effect<...> (+1 overload)

@since2.0.0

gen
(function* () {
var console: Console

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

The module exports two specific components:

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

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

Example using the global console:

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

Example using the Console class:

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

@seesource

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

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

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

See util.format() for more information.

@sincev0.1.100

log
(
"[Database] creating entry for bucket" +
`${
bucket: Bucket
bucket
.
Bucket.name: string
name
} and index ${
index: Index
index
.
Index.id: string
id
}`
)
if (
const failureCase: FailureCaseLiterals
failureCase
=== "Database") {
return yield*
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const fail: <DatabaseError>(error: DatabaseError) => Effect.Effect<never, DatabaseError, never>

Creates an Effect that represents a recoverable error.

This Effect does not succeed but instead fails with the provided error. The failure can be of any type, and will propagate through the effect pipeline unless handled.

Use this function when you want to explicitly signal an error in an Effect computation. The failed effect can later be handled with functions like

catchAll

or

catchTag

.

@example

import { Effect } from "effect"

// Example of creating a failed effect const failedEffect = Effect.fail("Something went wrong")

// Handle the failure failedEffect.pipe( Effect.catchAll((error) => Effect.succeed(Recovered from: ${error})), Effect.runPromise ).then(console.log) // Output: "Recovered from: Something went wrong"

@since2.0.0

fail
(new
constructor DatabaseError(): DatabaseError
DatabaseError
())
} else {
return {
id: string
id
: "<entry.id>" }
}
}),
deleteEntry: (entry: Entry) => Effect.Effect<void, never, never>
deleteEntry
: (
entry: Entry
entry
) =>
import Console
Console
.
const log: (...args: ReadonlyArray<any>) => Effect.Effect<void>

@since2.0.0

log
(`[Database] delete entry ${
entry: Entry
entry
.
Entry.id: string
id
}`)
}
})
)
// Merge all the test layers for S3, ElasticSearch, and Database
// services into a single layer
const
const layer: Layer.Layer<S3 | ElasticSearch | Database, never, FailureCase>
layer
=
import Layer
Layer
.
const mergeAll: <[Layer.Layer<S3, never, FailureCase>, Layer.Layer<ElasticSearch, never, FailureCase>, Layer.Layer<Database, never, FailureCase>]>(layers_0: Layer.Layer<...>, layers_1: Layer.Layer<...>, layers_2: Layer.Layer<...>) => Layer.Layer<...>

Combines all the provided layers concurrently, creating a new layer with merged input, error, and output types.

@since2.0.0

mergeAll
(
const S3Test: Layer.Layer<S3, never, FailureCase>
S3Test
,
const ElasticSearchTest: Layer.Layer<ElasticSearch, never, FailureCase>
ElasticSearchTest
,
const DatabaseTest: Layer.Layer<Database, never, FailureCase>
DatabaseTest
)
// Create a runnable effect to test the Workspace code. The effect is
// provided with the test layer and a FailureCase service with undefined
// value (no failure case).
const
const runnable: Effect.Effect<Entry, S3Error | ElasticSearchError | DatabaseError, never>
runnable
=
const make: Effect.Effect<Entry, S3Error | ElasticSearchError | DatabaseError, S3 | ElasticSearch | Database>
make
.
Pipeable.pipe<Effect.Effect<Entry, S3Error | ElasticSearchError | DatabaseError, S3 | ElasticSearch | Database>, Effect.Effect<...>, Effect.Effect<...>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<...>) => Effect.Effect<...>, bc: (_: Effect.Effect<...>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const provide: <S3 | ElasticSearch | Database, never, FailureCase>(layer: Layer.Layer<S3 | ElasticSearch | Database, never, FailureCase>) => <A, E, R>(self: Effect.Effect<...>) => Effect.Effect<...> (+9 overloads)

Splits the context into two parts, providing one part using the specified layer/context/runtime and leaving the remainder R0

@since2.0.0

provide
(
const layer: Layer.Layer<S3 | ElasticSearch | Database, never, FailureCase>
layer
),
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const provideService: <typeof FailureCase>(tag: typeof FailureCase, service: FailureCaseLiterals) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<...> (+1 overload)

Provides the effect with the single service it requires. If the effect requires more than one service use provide instead.

@since2.0.0

provideService
(
class FailureCase
FailureCase
,
var undefined
undefined
)
)
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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

Executes an effect and returns a Promise that resolves with the result.

Use runPromise when working with asynchronous effects and you need to integrate with code that uses Promises. If the effect fails, the returned Promise will be rejected with the error.

@example

import { Effect } from "effect"

// Execute an effect and handle the result with a Promise Effect.runPromise(Effect.succeed(1)).then(console.log) // Output: 1

// Execute a failing effect and handle the rejection Effect.runPromise(Effect.fail("my error")).catch((error) => { console.error("Effect failed with error:", error) })

@since2.0.0

runPromise
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const either: <Entry, S3Error | ElasticSearchError | DatabaseError, never>(self: Effect.Effect<Entry, S3Error | ElasticSearchError | DatabaseError, never>) => Effect.Effect<...>

Returns an effect whose failure and success have been lifted into an Either. The resulting effect cannot fail, because the failure case has been exposed as part of the Either success case.

This method is useful for recovering from effects that may fail.

The error parameter of the returned Effect is never, since it is guaranteed the effect does not model failure.

@since2.0.0

either
(
const runnable: Effect.Effect<Entry, S3Error | ElasticSearchError | DatabaseError, never>
runnable
)).
Promise<Either<Entry, S3Error | ElasticSearchError | DatabaseError>>.then<void, never>(onfulfilled?: ((value: Either<Entry, S3Error | ElasticSearchError | DatabaseError>) => void | PromiseLike<void>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<...>) | ... 1 more ... | undefined): Promise<...>

Attaches callbacks for the resolution and/or rejection of the Promise.

@paramonfulfilled The callback to execute when the Promise is resolved.

@paramonrejected The callback to execute when the Promise is rejected.

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

Example using the Console class:

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

@seesource

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

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

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

See util.format() for more information.

@sincev0.1.100

log
)

Let’s examine the test results for the scenario where FailureCase is set to undefined (happy path):

Terminal window
[S3] creating bucket
[ElasticSearch] creating index
[Database] creating entry for bucket <bucket.name> and index <index.id>
{
_id: "Either",
_tag: "Right",
right: {
id: "<entry.id>"
}
}

In this case, all operations succeed, and we see a successful result with right({ id: '<entry.id>' }).

Now, let’s simulate a failure in the Database:

const runnable = make.pipe(
Effect.provide(layer),
Effect.provideService(FailureCase, "Database")
)

The console output will be:

Terminal window
[S3] creating bucket
[ElasticSearch] creating index
[Database] creating entry for bucket <bucket.name> and index <index.id>
[ElasticSearch] delete index <index.id>
[S3] delete bucket <bucket.name>
{
_id: "Either",
_tag: "Left",
left: {
_tag: "DatabaseError"
}
}

You can observe that once the Database error occurs, there is a complete rollback that deletes the ElasticSearch index first and then the associated S3 bucket. The result is a failure with left(new DatabaseError()).

Let’s now make the index creation fail instead:

const runnable = make.pipe(
Effect.provide(layer),
Effect.provideService(FailureCase, "ElasticSearch")
)

In this case, the console output will be:

Terminal window
[S3] creating bucket
[ElasticSearch] creating index
[S3] delete bucket <bucket.name>
{
_id: "Either",
_tag: "Left",
left: {
_tag: "ElasticSearchError"
}
}

As expected, once the ElasticSearch index creation fails, there is a rollback that deletes the S3 bucket. The result is a failure with left(new ElasticSearchError()).