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
1
import {
import Effect
Effect
,
import Context
Context
} from "effect"
2
3
class
class S3Error
S3Error
{
4
readonly
(property) S3Error._tag: "S3Error"
_tag
= "S3Error"
5
}
6
7
interface
interface Bucket
Bucket
{
8
readonly
(property) Bucket.name: string
name
: string
9
}
10
11
class
class S3
S3
extends
import Context
Context
.
const Tag: <"S3">(id: "S3") => <Self, Shape>() => Context.TagClass<Self, "S3", Shape> namespace Tag
Tag
("S3")<
12
class S3
S3
,
13
{
14
readonly
(property) createBucket: Effect.Effect<Bucket, S3Error, never>
createBucket
:
import Effect
Effect
.
interface Effect<out A, out E = never, out R = never> namespace Effect

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.

Effect
<
interface Bucket
Bucket
,
class S3Error
S3Error
>
15
readonly
(property) deleteBucket: (bucket: Bucket) => Effect.Effect<void>
deleteBucket
: (
(parameter) bucket: Bucket
bucket
:
interface Bucket
Bucket
) =>
import Effect
Effect
.
interface Effect<out A, out E = never, out R = never> namespace Effect

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.

Effect
<void>
16
}
17
>() {}
18
19
class
class ElasticSearchError
ElasticSearchError
{
20
readonly
(property) ElasticSearchError._tag: "ElasticSearchError"
_tag
= "ElasticSearchError"
21
}
22
23
interface
interface Index
Index
{
24
readonly
(property) Index.id: string
id
: string
25
}
26
27
class
class ElasticSearch
ElasticSearch
extends
import Context
Context
.
const Tag: <"ElasticSearch">(id: "ElasticSearch") => <Self, Shape>() => Context.TagClass<Self, "ElasticSearch", Shape> namespace Tag
Tag
("ElasticSearch")<
28
class ElasticSearch
ElasticSearch
,
29
{
30
readonly
(property) createIndex: Effect.Effect<Index, ElasticSearchError, never>
createIndex
:
import Effect
Effect
.
interface Effect<out A, out E = never, out R = never> namespace Effect

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.

Effect
<
interface Index
Index
,
class ElasticSearchError
ElasticSearchError
>
31
readonly
(property) deleteIndex: (index: Index) => Effect.Effect<void>
deleteIndex
: (
(parameter) index: Index
index
:
interface Index
Index
) =>
import Effect
Effect
.
interface Effect<out A, out E = never, out R = never> namespace Effect

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.

Effect
<void>
32
}
33
>() {}
34
35
class
class DatabaseError
DatabaseError
{
36
readonly
(property) DatabaseError._tag: "DatabaseError"
_tag
= "DatabaseError"
37
}
38
39
interface
interface Entry
Entry
{
40
readonly
(property) Entry.id: string
id
: string
41
}
42
43
class
class Database
Database
extends
import Context
Context
.
const Tag: <"Database">(id: "Database") => <Self, Shape>() => Context.TagClass<Self, "Database", Shape> namespace Tag
Tag
("Database")<
44
class Database
Database
,
45
{
46
readonly
(property) createEntry: (bucket: Bucket, index: Index) => Effect.Effect<Entry, DatabaseError>
createEntry
: (
47
(parameter) bucket: Bucket
bucket
:
interface Bucket
Bucket
,
48
(parameter) index: Index
index
:
interface Index
Index
49
) =>
import Effect
Effect
.
interface Effect<out A, out E = never, out R = never> namespace Effect

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.

Effect
<
interface Entry
Entry
,
class DatabaseError
DatabaseError
>
50
readonly
(property) deleteEntry: (entry: Entry) => Effect.Effect<void>
deleteEntry
: (
(parameter) entry: Entry
entry
:
interface Entry
Entry
) =>
import Effect
Effect
.
interface Effect<out A, out E = never, out R = never> namespace Effect

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.

Effect
<void>
51
}
52
>() {}

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

1
import {
import Effect
Effect
,
import Context
Context
,
import Exit
Exit
} from "effect"
2
50 collapsed lines
3
class
class S3Error
S3Error
{
4
readonly
(property) S3Error._tag: "S3Error"
_tag
= "S3Error"
5
}
6
7
interface
interface Bucket
Bucket
{
8
readonly
(property) Bucket.name: string
name
: string
9
}
10
11
class
class S3
S3
extends
import Context
Context
.
const Tag: <"S3">(id: "S3") => <Self, Shape>() => Context.TagClass<Self, "S3", Shape> namespace Tag
Tag
("S3")<
12
class S3
S3
,
13
{
14
readonly
(property) createBucket: Effect.Effect<Bucket, S3Error, never>
createBucket
:
import Effect
Effect
.
interface Effect<out A, out E = never, out R = never> namespace Effect

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.

Effect
<
interface Bucket
Bucket
,
class S3Error
S3Error
>
15
readonly
(property) deleteBucket: (bucket: Bucket) => Effect.Effect<void>
deleteBucket
: (
(parameter) bucket: Bucket
bucket
:
interface Bucket
Bucket
) =>
import Effect
Effect
.
interface Effect<out A, out E = never, out R = never> namespace Effect

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.

Effect
<void>
16
}
17
>() {}
18
19
class
class ElasticSearchError
ElasticSearchError
{
20
readonly
(property) ElasticSearchError._tag: "ElasticSearchError"
_tag
= "ElasticSearchError"
21
}
22
23
interface
interface Index
Index
{
24
readonly
(property) Index.id: string
id
: string
25
}
26
27
class
class ElasticSearch
ElasticSearch
extends
import Context
Context
.
const Tag: <"ElasticSearch">(id: "ElasticSearch") => <Self, Shape>() => Context.TagClass<Self, "ElasticSearch", Shape> namespace Tag
Tag
("ElasticSearch")<
28
class ElasticSearch
ElasticSearch
,
29
{
30
readonly
(property) createIndex: Effect.Effect<Index, ElasticSearchError, never>
createIndex
:
import Effect
Effect
.
interface Effect<out A, out E = never, out R = never> namespace Effect

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.

Effect
<
interface Index
Index
,
class ElasticSearchError
ElasticSearchError
>
31
readonly
(property) deleteIndex: (index: Index) => Effect.Effect<void>
deleteIndex
: (
(parameter) index: Index
index
:
interface Index
Index
) =>
import Effect
Effect
.
interface Effect<out A, out E = never, out R = never> namespace Effect

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.

Effect
<void>
32
}
33
>() {}
34
35
class
class DatabaseError
DatabaseError
{
36
readonly
(property) DatabaseError._tag: "DatabaseError"
_tag
= "DatabaseError"
37
}
38
39
interface
interface Entry
Entry
{
40
readonly
(property) Entry.id: string
id
: string
41
}
42
43
class
class Database
Database
extends
import Context
Context
.
const Tag: <"Database">(id: "Database") => <Self, Shape>() => Context.TagClass<Self, "Database", Shape> namespace Tag
Tag
("Database")<
44
class Database
Database
,
45
{
46
readonly
(property) createEntry: (bucket: Bucket, index: Index) => Effect.Effect<Entry, DatabaseError>
createEntry
: (
47
(parameter) bucket: Bucket
bucket
:
interface Bucket
Bucket
,
48
(parameter) index: Index
index
:
interface Index
Index
49
) =>
import Effect
Effect
.
interface Effect<out A, out E = never, out R = never> namespace Effect

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.

Effect
<
interface Entry
Entry
,
class DatabaseError
DatabaseError
>
50
readonly
(property) deleteEntry: (entry: Entry) => Effect.Effect<void>
deleteEntry
: (
(parameter) entry: Entry
entry
:
interface Entry
Entry
) =>
import Effect
Effect
.
interface Effect<out A, out E = never, out R = never> namespace Effect

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.

Effect
<void>
51
}
52
>() {}
53
54
// Create a bucket, and define the release function that deletes the
55
// bucket if the operation fails.
56
const
const createBucket: Effect.Effect<Bucket, S3Error, S3 | Scope>
createBucket
=
import Effect
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)
gen
(function* () {
57
const {
const createBucket: Effect.Effect<Bucket, S3Error, never>
createBucket
,
const deleteBucket: (bucket: Bucket) => Effect.Effect<void>
deleteBucket
} = yield*
class S3
S3
58
return yield*
import Effect
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.

acquireRelease
(
const createBucket: Effect.Effect<Bucket, S3Error, never>
createBucket
, (
(parameter) bucket: Bucket
bucket
,
(parameter) exit: Exit.Exit<unknown, unknown>
exit
) =>
59
// The release function for the Effect.acquireRelease operation is
60
// responsible for handling the acquired resource (bucket) after the
61
// main effect has completed. It is called regardless of whether the
62
// main effect succeeded or failed. If the main effect failed,
63
// Exit.isFailure(exit) will be true, and the function will perform
64
// a rollback by calling deleteBucket(bucket). If the main effect
65
// succeeded, Exit.isFailure(exit) will be false, and the function
66
// will return Effect.void, representing a successful, but
67
// do-nothing effect.
68
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.

isFailure
(
(parameter) exit: Exit.Exit<unknown, unknown>
exit
) ?
const deleteBucket: (bucket: Bucket) => Effect.Effect<void>
deleteBucket
(
(parameter) bucket: Bucket
bucket
) :
import Effect
Effect
.
(alias) const void: Effect.Effect<void, never, never> export void
void
69
)
70
})
71
72
// Create an index, and define the release function that deletes the
73
// index if the operation fails.
74
const
const createIndex: Effect.Effect<Index, ElasticSearchError, ElasticSearch | Scope>
createIndex
=
import Effect
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)
gen
(function* () {
75
const {
const createIndex: Effect.Effect<Index, ElasticSearchError, never>
createIndex
,
const deleteIndex: (index: Index) => Effect.Effect<void>
deleteIndex
} = yield*
class ElasticSearch
ElasticSearch
76
return yield*
import Effect
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.

acquireRelease
(
const createIndex: Effect.Effect<Index, ElasticSearchError, never>
createIndex
, (
(parameter) index: Index
index
,
(parameter) exit: Exit.Exit<unknown, unknown>
exit
) =>
77
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.

isFailure
(
(parameter) exit: Exit.Exit<unknown, unknown>
exit
) ?
const deleteIndex: (index: Index) => Effect.Effect<void>
deleteIndex
(
(parameter) index: Index
index
) :
import Effect
Effect
.
(alias) const void: Effect.Effect<void, never, never> export void
void
78
)
79
})
80
81
// Create an entry in the database, and define the release function that
82
// deletes the entry if the operation fails.
83
const
const createEntry: (bucket: Bucket, index: Index) => Effect.Effect<Entry, DatabaseError, Database | Scope>
createEntry
= (
(parameter) bucket: Bucket
bucket
:
interface Bucket
Bucket
,
(parameter) index: Index
index
:
interface Index
Index
) =>
84
import Effect
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)
gen
(function* () {
85
const {
const createEntry: (bucket: Bucket, index: Index) => Effect.Effect<Entry, DatabaseError>
createEntry
,
const deleteEntry: (entry: Entry) => Effect.Effect<void>
deleteEntry
} = yield*
class Database
Database
86
return yield*
import Effect
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.

acquireRelease
(
87
const createEntry: (bucket: Bucket, index: Index) => Effect.Effect<Entry, DatabaseError>
createEntry
(
(parameter) bucket: Bucket
bucket
,
(parameter) index: Index
index
),
88
(
(parameter) entry: Entry
entry
,
(parameter) exit: Exit.Exit<unknown, unknown>
exit
) =>
89
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.

isFailure
(
(parameter) exit: Exit.Exit<unknown, unknown>
exit
) ?
const deleteEntry: (entry: Entry) => Effect.Effect<void>
deleteEntry
(
(parameter) entry: Entry
entry
) :
import Effect
Effect
.
(alias) const void: Effect.Effect<void, never, never> export void
void
90
)
91
})
92
93
const
const make: Effect.Effect<Entry, S3Error | ElasticSearchError | DatabaseError, S3 | ElasticSearch | Database>
make
=
import Effect
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.

scoped
(
94
import Effect
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)
gen
(function* () {
95
const
const bucket: Bucket
bucket
= yield*
const createBucket: Effect.Effect<Bucket, S3Error, S3 | Scope>
createBucket
96
const
const index: Index
index
= yield*
const createIndex: Effect.Effect<Index, ElasticSearchError, ElasticSearch | Scope>
createIndex
97
return yield*
const createEntry: (bucket: Bucket, index: Index) => Effect.Effect<Entry, DatabaseError, Database | Scope>
createEntry
(
const bucket: Bucket
bucket
,
const index: Index
index
)
98
})
99
)

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.

1
import {
import Effect
Effect
,
import Context
Context
,
import Layer
Layer
,
import Console
Console
,
import Exit
Exit
} from "effect"
2
97 collapsed lines
3
class
class S3Error
S3Error
{
4
readonly
(property) S3Error._tag: "S3Error"
_tag
= "S3Error"
5
}
6
7
interface
interface Bucket
Bucket
{
8
readonly
(property) Bucket.name: string
name
: string
9
}
10
11
class
class S3
S3
extends
import Context
Context
.
const Tag: <"S3">(id: "S3") => <Self, Shape>() => Context.TagClass<Self, "S3", Shape> namespace Tag
Tag
("S3")<
12
class S3
S3
,
13
{
14
readonly
(property) createBucket: Effect.Effect<Bucket, S3Error, never>
createBucket
:
import Effect
Effect
.
interface Effect<out A, out E = never, out R = never> namespace Effect

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.

Effect
<
interface Bucket
Bucket
,
class S3Error
S3Error
>
15
readonly
(property) deleteBucket: (bucket: Bucket) => Effect.Effect<void>
deleteBucket
: (
(parameter) bucket: Bucket
bucket
:
interface Bucket
Bucket
) =>
import Effect
Effect
.
interface Effect<out A, out E = never, out R = never> namespace Effect

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.

Effect
<void>
16
}
17
>() {}
18
19
class
class ElasticSearchError
ElasticSearchError
{
20
readonly
(property) ElasticSearchError._tag: "ElasticSearchError"
_tag
= "ElasticSearchError"
21
}
22
23
interface
interface Index
Index
{
24
readonly
(property) Index.id: string
id
: string
25
}
26
27
class
class ElasticSearch
ElasticSearch
extends
import Context
Context
.
const Tag: <"ElasticSearch">(id: "ElasticSearch") => <Self, Shape>() => Context.TagClass<Self, "ElasticSearch", Shape> namespace Tag
Tag
("ElasticSearch")<
28
class ElasticSearch
ElasticSearch
,
29
{
30
readonly
(property) createIndex: Effect.Effect<Index, ElasticSearchError, never>
createIndex
:
import Effect
Effect
.
interface Effect<out A, out E = never, out R = never> namespace Effect

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.

Effect
<
interface Index
Index
,
class ElasticSearchError
ElasticSearchError
>
31
readonly
(property) deleteIndex: (index: Index) => Effect.Effect<void>
deleteIndex
: (
(parameter) index: Index
index
:
interface Index
Index
) =>
import Effect
Effect
.
interface Effect<out A, out E = never, out R = never> namespace Effect

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.

Effect
<void>
32
}
33
>() {}
34
35
class
class DatabaseError
DatabaseError
{
36
readonly
(property) DatabaseError._tag: "DatabaseError"
_tag
= "DatabaseError"
37
}
38
39
interface
interface Entry
Entry
{
40
readonly
(property) Entry.id: string
id
: string
41
}
42
43
class
class Database
Database
extends
import Context
Context
.
const Tag: <"Database">(id: "Database") => <Self, Shape>() => Context.TagClass<Self, "Database", Shape> namespace Tag
Tag
("Database")<
44
class Database
Database
,
45
{
46
readonly
(property) createEntry: (bucket: Bucket, index: Index) => Effect.Effect<Entry, DatabaseError>
createEntry
: (
47
(parameter) bucket: Bucket
bucket
:
interface Bucket
Bucket
,
48
(parameter) index: Index
index
:
interface Index
Index
49
) =>
import Effect
Effect
.
interface Effect<out A, out E = never, out R = never> namespace Effect

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.

Effect
<
interface Entry
Entry
,
class DatabaseError
DatabaseError
>
50
readonly
(property) deleteEntry: (entry: Entry) => Effect.Effect<void>
deleteEntry
: (
(parameter) entry: Entry
entry
:
interface Entry
Entry
) =>
import Effect
Effect
.
interface Effect<out A, out E = never, out R = never> namespace Effect

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.

Effect
<void>
51
}
52
>() {}
53
54
// Create a bucket, and define the release function that deletes the
55
// bucket if the operation fails.
56
const
const createBucket: Effect.Effect<Bucket, S3Error, S3 | Scope>
createBucket
=
import Effect
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)
gen
(function* () {
57
const {
const createBucket: Effect.Effect<Bucket, S3Error, never>
createBucket
,
const deleteBucket: (bucket: Bucket) => Effect.Effect<void>
deleteBucket
} = yield*
class S3
S3
58
return yield*
import Effect
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.

acquireRelease
(
const createBucket: Effect.Effect<Bucket, S3Error, never>
createBucket
, (
(parameter) bucket: Bucket
bucket
,
(parameter) exit: Exit.Exit<unknown, unknown>
exit
) =>
59
// The release function for the Effect.acquireRelease operation is
60
// responsible for handling the acquired resource (bucket) after the
61
// main effect has completed. It is called regardless of whether the
62
// main effect succeeded or failed. If the main effect failed,
63
// Exit.isFailure(exit) will be true, and the function will perform
64
// a rollback by calling deleteBucket(bucket). If the main effect
65
// succeeded, Exit.isFailure(exit) will be false, and the function
66
// will return Effect.void, representing a successful, but
67
// do-nothing effect.
68
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.

isFailure
(
(parameter) exit: Exit.Exit<unknown, unknown>
exit
) ?
const deleteBucket: (bucket: Bucket) => Effect.Effect<void>
deleteBucket
(
(parameter) bucket: Bucket
bucket
) :
import Effect
Effect
.
(alias) const void: Effect.Effect<void, never, never> export void
void
69
)
70
})
71
72
// Create an index, and define the release function that deletes the
73
// index if the operation fails.
74
const
const createIndex: Effect.Effect<Index, ElasticSearchError, ElasticSearch | Scope>
createIndex
=
import Effect
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)
gen
(function* () {
75
const {
const createIndex: Effect.Effect<Index, ElasticSearchError, never>
createIndex
,
const deleteIndex: (index: Index) => Effect.Effect<void>
deleteIndex
} = yield*
class ElasticSearch
ElasticSearch
76
return yield*
import Effect
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.

acquireRelease
(
const createIndex: Effect.Effect<Index, ElasticSearchError, never>
createIndex
, (
(parameter) index: Index
index
,
(parameter) exit: Exit.Exit<unknown, unknown>
exit
) =>
77
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.

isFailure
(
(parameter) exit: Exit.Exit<unknown, unknown>
exit
) ?
const deleteIndex: (index: Index) => Effect.Effect<void>
deleteIndex
(
(parameter) index: Index
index
) :
import Effect
Effect
.
(alias) const void: Effect.Effect<void, never, never> export void
void
78
)
79
})
80
81
// Create an entry in the database, and define the release function that
82
// deletes the entry if the operation fails.
83
const
const createEntry: (bucket: Bucket, index: Index) => Effect.Effect<Entry, DatabaseError, Database | Scope>
createEntry
= (
(parameter) bucket: Bucket
bucket
:
interface Bucket
Bucket
,
(parameter) index: Index
index
:
interface Index
Index
) =>
84
import Effect
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)
gen
(function* () {
85
const {
const createEntry: (bucket: Bucket, index: Index) => Effect.Effect<Entry, DatabaseError>
createEntry
,
const deleteEntry: (entry: Entry) => Effect.Effect<void>
deleteEntry
} = yield*
class Database
Database
86
return yield*
import Effect
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.

acquireRelease
(
87
const createEntry: (bucket: Bucket, index: Index) => Effect.Effect<Entry, DatabaseError>
createEntry
(
(parameter) bucket: Bucket
bucket
,
(parameter) index: Index
index
),
88
(
(parameter) entry: Entry
entry
,
(parameter) exit: Exit.Exit<unknown, unknown>
exit
) =>
89
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.

isFailure
(
(parameter) exit: Exit.Exit<unknown, unknown>
exit
) ?
const deleteEntry: (entry: Entry) => Effect.Effect<void>
deleteEntry
(
(parameter) entry: Entry
entry
) :
import Effect
Effect
.
(alias) const void: Effect.Effect<void, never, never> export void
void
90
)
91
})
92
93
const
const make: Effect.Effect<Entry, S3Error | ElasticSearchError | DatabaseError, S3 | ElasticSearch | Database>
make
=
import Effect
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.

scoped
(
94
import Effect
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)
gen
(function* () {
95
const
const bucket: Bucket
bucket
= yield*
const createBucket: Effect.Effect<Bucket, S3Error, S3 | Scope>
createBucket
96
const
const index: Index
index
= yield*
const createIndex: Effect.Effect<Index, ElasticSearchError, ElasticSearch | Scope>
createIndex
97
return yield*
const createEntry: (bucket: Bucket, index: Index) => Effect.Effect<Entry, DatabaseError, Database | Scope>
createEntry
(
const bucket: Bucket
bucket
,
const index: Index
index
)
98
})
99
)
100
101
// The `FailureCaseLiterals` type allows us to provide different error
102
// scenarios while testing our
103
//
104
// For example, by providing the value "S3", we can simulate an error
105
// scenario specific to the S3 service. This helps us ensure that our
106
// program handles errors correctly and behaves as expected in various
107
// situations.
108
//
109
// Similarly, we can provide other values like "ElasticSearch" or
110
// "Database" to simulate error scenarios for those In cases
111
// where we want to test the absence of errors, we can provide
112
// `undefined`. By using this parameter, we can thoroughly test our
113
// services and verify their behavior under different error conditions.
114
type
type FailureCaseLiterals = "S3" | "ElasticSearch" | "Database" | undefined
FailureCaseLiterals
= "S3" | "ElasticSearch" | "Database" | undefined
115
116
class
class FailureCase
FailureCase
extends
import Context
Context
.
const Tag: <"FailureCase">(id: "FailureCase") => <Self, Shape>() => Context.TagClass<Self, "FailureCase", Shape> namespace Tag
Tag
("FailureCase")<
117
class FailureCase
FailureCase
,
118
type FailureCaseLiterals = "S3" | "ElasticSearch" | "Database" | undefined
FailureCaseLiterals
119
>() {}
120
121
// Create a test layer for the S3 service
122
123
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.

effect
(
124
class S3
S3
,
125
import Effect
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)
gen
(function* () {
126
const
const failureCase: FailureCaseLiterals
failureCase
= yield*
class FailureCase
FailureCase
127
return {
128
(property) createBucket: Effect.Effect<{ name: string; }, S3Error, never>
createBucket
:
import Effect
Effect
.
const gen: <YieldWrap<Effect.Effect<never, S3Error, never>>, { name: string; }>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<never, S3Error, never>>, { ...; }, never>) => Effect.Effect<...> (+1 overload)
gen
(function* () {
129
namespace console 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`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). 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`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js 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: ```js 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 ```

console
.
(method) 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)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)). ```js 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()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.

log
("[S3] creating bucket")
130
if (
const failureCase: FailureCaseLiterals
failureCase
=== "S3") {
131
return yield*
import Effect
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 {@link catchAll } or {@link catchTag } .

fail
(new
constructor S3Error(): S3Error
S3Error
())
132
} else {
133
return {
(property) name: string
name
: "<bucket.name>" }
134
}
135
}),
136
(property) deleteBucket: (bucket: Bucket) => Effect.Effect<void, never, never>
deleteBucket
: (
(parameter) bucket: Bucket
bucket
) =>
137
import Console
Console
.
const log: (...args: ReadonlyArray<any>) => Effect.Effect<void>
log
(`[S3] delete bucket ${
(parameter) bucket: Bucket
bucket
.
(property) Bucket.name: string
name
}`)
138
}
139
})
140
)
141
142
// Create a test layer for the ElasticSearch service
143
144
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.

effect
(
145
class ElasticSearch
ElasticSearch
,
146
import Effect
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)
gen
(function* () {
147
const
const failureCase: FailureCaseLiterals
failureCase
= yield*
class FailureCase
FailureCase
148
return {
149
(property) createIndex: Effect.Effect<{ id: string; }, ElasticSearchError, never>
createIndex
:
import Effect
Effect
.
const gen: <YieldWrap<Effect.Effect<never, ElasticSearchError, never>>, { id: string; }>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<never, ElasticSearchError, never>>, { ...; }, never>) => Effect.Effect<...> (+1 overload)
gen
(function* () {
150
namespace console 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`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). 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`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js 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: ```js 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 ```

console
.
(method) 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)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)). ```js 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()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.

log
("[ElasticSearch] creating index")
151
if (
const failureCase: FailureCaseLiterals
failureCase
=== "ElasticSearch") {
152
return yield*
import Effect
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 {@link catchAll } or {@link catchTag } .

fail
(new
constructor ElasticSearchError(): ElasticSearchError
ElasticSearchError
())
153
} else {
154
return {
(property) id: string
id
: "<index.id>" }
155
}
156
}),
157
(property) deleteIndex: (index: Index) => Effect.Effect<void, never, never>
deleteIndex
: (
(parameter) index: Index
index
) =>
158
import Console
Console
.
const log: (...args: ReadonlyArray<any>) => Effect.Effect<void>
log
(`[ElasticSearch] delete index ${
(parameter) index: Index
index
.
(property) Index.id: string
id
}`)
159
}
160
})
161
)
162
163
// Create a test layer for the Database service
164
165
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.

effect
(
166
class Database
Database
,
167
import Effect
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)
gen
(function* () {
168
const
const failureCase: FailureCaseLiterals
failureCase
= yield*
class FailureCase
FailureCase
169
return {
170
(property) createEntry: (bucket: Bucket, index: Index) => Effect.Effect<{ id: string; }, DatabaseError, never>
createEntry
: (
(parameter) bucket: Bucket
bucket
,
(parameter) index: Index
index
) =>
171
import Effect
Effect
.
const gen: <YieldWrap<Effect.Effect<never, DatabaseError, never>>, { id: string; }>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<never, DatabaseError, never>>, { ...; }, never>) => Effect.Effect<...> (+1 overload)
gen
(function* () {
172
namespace console 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`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). 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`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js 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: ```js 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 ```

console
.
(method) 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)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)). ```js 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()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.

log
(
173
"[Database] creating entry for bucket" +
174
`${
(parameter) bucket: Bucket
bucket
.
(property) Bucket.name: string
name
} and index ${
(parameter) index: Index
index
.
(property) Index.id: string
id
}`
175
)
176
if (
const failureCase: FailureCaseLiterals
failureCase
=== "Database") {
177
return yield*
import Effect
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 {@link catchAll } or {@link catchTag } .

fail
(new
constructor DatabaseError(): DatabaseError
DatabaseError
())
178
} else {
179
return {
(property) id: string
id
: "<entry.id>" }
180
}
181
}),
182
(property) deleteEntry: (entry: Entry) => Effect.Effect<void, never, never>
deleteEntry
: (
(parameter) entry: Entry
entry
) =>
183
import Console
Console
.
const log: (...args: ReadonlyArray<any>) => Effect.Effect<void>
log
(`[Database] delete entry ${
(parameter) entry: Entry
entry
.
(property) Entry.id: string
id
}`)
184
}
185
})
186
)
187
188
// Merge all the test layers for S3, ElasticSearch, and Database
189
// services into a single layer
190
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.

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
)
191
192
// Create a runnable effect to test the Workspace code. The effect is
193
// provided with the test layer and a FailureCase service with undefined
194
// value (no failure case).
195
const
const runnable: Effect.Effect<Entry, S3Error | ElasticSearchError | DatabaseError, never>
runnable
=
const make: Effect.Effect<Entry, S3Error | ElasticSearchError | DatabaseError, S3 | ElasticSearch | Database>
make
.
(method) 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
(
196
import Effect
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`

provide
(
const layer: Layer.Layer<S3 | ElasticSearch | Database, never, FailureCase>
layer
),
197
import Effect
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.

provideService
(
class FailureCase
FailureCase
,
var undefined
undefined
)
198
)
199
200
import Effect
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.

runPromise
(
import Effect
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.

either
(
const runnable: Effect.Effect<Entry, S3Error | ElasticSearchError | DatabaseError, never>
runnable
)).
(method) 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.

then
(
namespace console 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`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). 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`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js 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: ```js 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 ```

console
.
(method) 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)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)). ```js 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()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.

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()).