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)

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

When to Use

gen allows you to write code that looks and behaves like synchronous code, but it can handle asynchronous tasks, errors, and complex control flow (like loops and conditions). It helps make asynchronous code more readable and easier to manage.

The generator functions work similarly to async/await but with more explicit control over the execution of effects. You can yield* values from effects and return the final result at the end.

@example

import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: Effect.succeed(total - (total * discountRate) / 100)
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
export const program = Effect.gen(function* () {
const transactionAmount = yield* fetchTransactionAmount
const discountRate = yield* fetchDiscountRate
const discountedAmount = yield* applyDiscount(
transactionAmount,
discountRate
)
const finalAmount = addServiceCharge(discountedAmount)
return `Final amount to charge: ${finalAmount}`
})

@since2.0.0

gen
(function* () {
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.

@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)

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

When to Use

gen allows you to write code that looks and behaves like synchronous code, but it can handle asynchronous tasks, errors, and complex control flow (like loops and conditions). It helps make asynchronous code more readable and easier to manage.

The generator functions work similarly to async/await but with more explicit control over the execution of effects. You can yield* values from effects and return the final result at the end.

@example

import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: Effect.succeed(total - (total * discountRate) / 100)
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
export const program = Effect.gen(function* () {
const transactionAmount = yield* fetchTransactionAmount
const discountRate = yield* fetchDiscountRate
const discountedAmount = yield* applyDiscount(
transactionAmount,
discountRate
)
const finalAmount = addServiceCharge(discountedAmount)
return `Final amount to charge: ${finalAmount}`
})

@since2.0.0

gen
(function* () {
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.

@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)

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

When to Use

gen allows you to write code that looks and behaves like synchronous code, but it can handle asynchronous tasks, errors, and complex control flow (like loops and conditions). It helps make asynchronous code more readable and easier to manage.

The generator functions work similarly to async/await but with more explicit control over the execution of effects. You can yield* values from effects and return the final result at the end.

@example

import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: Effect.succeed(total - (total * discountRate) / 100)
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
export const program = Effect.gen(function* () {
const transactionAmount = yield* fetchTransactionAmount
const discountRate = yield* fetchDiscountRate
const discountedAmount = yield* applyDiscount(
transactionAmount,
discountRate
)
const finalAmount = addServiceCharge(discountedAmount)
return `Final amount to charge: ${finalAmount}`
})

@since2.0.0

gen
(function* () {
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.

@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)

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

When to Use

gen allows you to write code that looks and behaves like synchronous code, but it can handle asynchronous tasks, errors, and complex control flow (like loops and conditions). It helps make asynchronous code more readable and easier to manage.

The generator functions work similarly to async/await but with more explicit control over the execution of effects. You can yield* values from effects and return the final result at the end.

@example

import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: Effect.succeed(total - (total * discountRate) / 100)
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
export const program = Effect.gen(function* () {
const transactionAmount = yield* fetchTransactionAmount
const discountRate = yield* fetchDiscountRate
const discountedAmount = yield* applyDiscount(
transactionAmount,
discountRate
)
const finalAmount = addServiceCharge(discountedAmount)
return `Final amount to charge: ${finalAmount}`
})

@since2.0.0

gen
(function* () {
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)

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

When to Use

gen allows you to write code that looks and behaves like synchronous code, but it can handle asynchronous tasks, errors, and complex control flow (like loops and conditions). It helps make asynchronous code more readable and easier to manage.

The generator functions work similarly to async/await but with more explicit control over the execution of effects. You can yield* values from effects and return the final result at the end.

@example

import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: Effect.succeed(total - (total * discountRate) / 100)
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
export const program = Effect.gen(function* () {
const transactionAmount = yield* fetchTransactionAmount
const discountRate = yield* fetchDiscountRate
const discountedAmount = yield* applyDiscount(
transactionAmount,
discountRate
)
const finalAmount = addServiceCharge(discountedAmount)
return `Final amount to charge: ${finalAmount}`
})

@since2.0.0

gen
(function* () {
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.

@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)

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

When to Use

gen allows you to write code that looks and behaves like synchronous code, but it can handle asynchronous tasks, errors, and complex control flow (like loops and conditions). It helps make asynchronous code more readable and easier to manage.

The generator functions work similarly to async/await but with more explicit control over the execution of effects. You can yield* values from effects and return the final result at the end.

@example

import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: Effect.succeed(total - (total * discountRate) / 100)
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
export const program = Effect.gen(function* () {
const transactionAmount = yield* fetchTransactionAmount
const discountRate = yield* fetchDiscountRate
const discountedAmount = yield* applyDiscount(
transactionAmount,
discountRate
)
const finalAmount = addServiceCharge(discountedAmount)
return `Final amount to charge: ${finalAmount}`
})

@since2.0.0

gen
(function* () {
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.

@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)

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

When to Use

gen allows you to write code that looks and behaves like synchronous code, but it can handle asynchronous tasks, errors, and complex control flow (like loops and conditions). It helps make asynchronous code more readable and easier to manage.

The generator functions work similarly to async/await but with more explicit control over the execution of effects. You can yield* values from effects and return the final result at the end.

@example

import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: Effect.succeed(total - (total * discountRate) / 100)
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
export const program = Effect.gen(function* () {
const transactionAmount = yield* fetchTransactionAmount
const discountRate = yield* fetchDiscountRate
const discountedAmount = yield* applyDiscount(
transactionAmount,
discountRate
)
const finalAmount = addServiceCharge(discountedAmount)
return `Final amount to charge: ${finalAmount}`
})

@since2.0.0

gen
(function* () {
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.

@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)

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

When to Use

gen allows you to write code that looks and behaves like synchronous code, but it can handle asynchronous tasks, errors, and complex control flow (like loops and conditions). It helps make asynchronous code more readable and easier to manage.

The generator functions work similarly to async/await but with more explicit control over the execution of effects. You can yield* values from effects and return the final result at the end.

@example

import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: Effect.succeed(total - (total * discountRate) / 100)
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
export const program = Effect.gen(function* () {
const transactionAmount = yield* fetchTransactionAmount
const discountRate = yield* fetchDiscountRate
const discountedAmount = yield* applyDiscount(
transactionAmount,
discountRate
)
const finalAmount = addServiceCharge(discountedAmount)
return `Final amount to charge: ${finalAmount}`
})

@since2.0.0

gen
(function* () {
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)

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

When to Use

gen allows you to write code that looks and behaves like synchronous code, but it can handle asynchronous tasks, errors, and complex control flow (like loops and conditions). It helps make asynchronous code more readable and easier to manage.

The generator functions work similarly to async/await but with more explicit control over the execution of effects. You can yield* values from effects and return the final result at the end.

@example

import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: Effect.succeed(total - (total * discountRate) / 100)
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
export const program = Effect.gen(function* () {
const transactionAmount = yield* fetchTransactionAmount
const discountRate = yield* fetchDiscountRate
const discountedAmount = yield* applyDiscount(
transactionAmount,
discountRate
)
const finalAmount = addServiceCharge(discountedAmount)
return `Final amount to charge: ${finalAmount}`
})

@since2.0.0

gen
(function* () {
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)

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

When to Use

gen allows you to write code that looks and behaves like synchronous code, but it can handle asynchronous tasks, errors, and complex control flow (like loops and conditions). It helps make asynchronous code more readable and easier to manage.

The generator functions work similarly to async/await but with more explicit control over the execution of effects. You can yield* values from effects and return the final result at the end.

@example

import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: Effect.succeed(total - (total * discountRate) / 100)
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
export const program = Effect.gen(function* () {
const transactionAmount = yield* fetchTransactionAmount
const discountRate = yield* fetchDiscountRate
const discountedAmount = yield* applyDiscount(
transactionAmount,
discountRate
)
const finalAmount = addServiceCharge(discountedAmount)
return `Final amount to charge: ${finalAmount}`
})

@since2.0.0

gen
(function* () {
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.

When to Use

Use this function to explicitly signal an error in an Effect. The error will keep propagating unless it is handled. You can handle the error with functions like

catchAll

or

catchTag

.

@seesucceed to create an effect that represents a successful value.

@example

// Title: Creating a Failed Effect
import { Effect } from "effect"
// ┌─── Effect<never, Error, never>
// ▼
const failure = Effect.fail(
new Error("Operation failed due to network error")
)

@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)

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

When to Use

gen allows you to write code that looks and behaves like synchronous code, but it can handle asynchronous tasks, errors, and complex control flow (like loops and conditions). It helps make asynchronous code more readable and easier to manage.

The generator functions work similarly to async/await but with more explicit control over the execution of effects. You can yield* values from effects and return the final result at the end.

@example

import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: Effect.succeed(total - (total * discountRate) / 100)
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
export const program = Effect.gen(function* () {
const transactionAmount = yield* fetchTransactionAmount
const discountRate = yield* fetchDiscountRate
const discountedAmount = yield* applyDiscount(
transactionAmount,
discountRate
)
const finalAmount = addServiceCharge(discountedAmount)
return `Final amount to charge: ${finalAmount}`
})

@since2.0.0

gen
(function* () {
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)

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

When to Use

gen allows you to write code that looks and behaves like synchronous code, but it can handle asynchronous tasks, errors, and complex control flow (like loops and conditions). It helps make asynchronous code more readable and easier to manage.

The generator functions work similarly to async/await but with more explicit control over the execution of effects. You can yield* values from effects and return the final result at the end.

@example

import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: Effect.succeed(total - (total * discountRate) / 100)
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
export const program = Effect.gen(function* () {
const transactionAmount = yield* fetchTransactionAmount
const discountRate = yield* fetchDiscountRate
const discountedAmount = yield* applyDiscount(
transactionAmount,
discountRate
)
const finalAmount = addServiceCharge(discountedAmount)
return `Final amount to charge: ${finalAmount}`
})

@since2.0.0

gen
(function* () {
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.

When to Use

Use this function to explicitly signal an error in an Effect. The error will keep propagating unless it is handled. You can handle the error with functions like

catchAll

or

catchTag

.

@seesucceed to create an effect that represents a successful value.

@example

// Title: Creating a Failed Effect
import { Effect } from "effect"
// ┌─── Effect<never, Error, never>
// ▼
const failure = Effect.fail(
new Error("Operation failed due to network error")
)

@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)

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

When to Use

gen allows you to write code that looks and behaves like synchronous code, but it can handle asynchronous tasks, errors, and complex control flow (like loops and conditions). It helps make asynchronous code more readable and easier to manage.

The generator functions work similarly to async/await but with more explicit control over the execution of effects. You can yield* values from effects and return the final result at the end.

@example

import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: Effect.succeed(total - (total * discountRate) / 100)
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
export const program = Effect.gen(function* () {
const transactionAmount = yield* fetchTransactionAmount
const discountRate = yield* fetchDiscountRate
const discountedAmount = yield* applyDiscount(
transactionAmount,
discountRate
)
const finalAmount = addServiceCharge(discountedAmount)
return `Final amount to charge: ${finalAmount}`
})

@since2.0.0

gen
(function* () {
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)

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

When to Use

gen allows you to write code that looks and behaves like synchronous code, but it can handle asynchronous tasks, errors, and complex control flow (like loops and conditions). It helps make asynchronous code more readable and easier to manage.

The generator functions work similarly to async/await but with more explicit control over the execution of effects. You can yield* values from effects and return the final result at the end.

@example

import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: Effect.succeed(total - (total * discountRate) / 100)
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
export const program = Effect.gen(function* () {
const transactionAmount = yield* fetchTransactionAmount
const discountRate = yield* fetchDiscountRate
const discountedAmount = yield* applyDiscount(
transactionAmount,
discountRate
)
const finalAmount = addServiceCharge(discountedAmount)
return `Final amount to charge: ${finalAmount}`
})

@since2.0.0

gen
(function* () {
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.

When to Use

Use this function to explicitly signal an error in an Effect. The error will keep propagating unless it is handled. You can handle the error with functions like

catchAll

or

catchTag

.

@seesucceed to create an effect that represents a successful value.

@example

// Title: Creating a Failed Effect
import { Effect } from "effect"
// ┌─── Effect<never, Error, never>
// ▼
const failure = Effect.fail(
new Error("Operation failed due to network error")
)

@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)

Provides the necessary Layers to an effect, removing its dependency on the environment.

You can pass multiple layers, a Context, Runtime, or ManagedRuntime to the effect.

@seeprovideService for providing a service to an effect.

@example

import { Context, Effect, Layer } from "effect"
class Database extends Context.Tag("Database")<
Database,
{ readonly query: (sql: string) => Effect.Effect<Array<unknown>> }
>() {}
const DatabaseLive = Layer.succeed(
Database,
{
// Simulate a database query
query: (sql: string) => Effect.log(`Executing query: ${sql}`).pipe(Effect.as([]))
}
)
// ┌─── Effect<unknown[], never, Database>
// ▼
const program = Effect.gen(function*() {
const database = yield* Database
const result = yield* database.query("SELECT * FROM users")
return result
})
// ┌─── Effect<unknown[], never, never>
// ▼
const runnable = Effect.provide(program, DatabaseLive)
Effect.runPromise(runnable).then(console.log)
// Output:
// timestamp=... level=INFO fiber=#0 message="Executing query: SELECT * FROM users"
// []

@since2.0.0

provide
(
const 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)

The provideService function is used to provide an actual implementation for a service in the context of an effect.

This function allows you to associate a service with its implementation so that it can be used in your program. You define the service (e.g., a random number generator), and then you use provideService to link that service to its implementation. Once the implementation is provided, the effect can be run successfully without further requirements.

@seeprovide for providing multiple layers to an effect.

@example

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

@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 the result as a Promise.

When to Use

Use runPromise when you need to execute an effect and work with the result using Promise syntax, typically for compatibility with other promise-based code.

If the effect succeeds, the promise will resolve with the result. If the effect fails, the promise will reject with an error.

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

@example

// Title: Running a Successful Effect as a Promise
import { Effect } from "effect"
Effect.runPromise(Effect.succeed(1)).then(console.log)
// Output: 1

@example

//Example: Handling a Failing Effect as a Rejected Promise import { Effect } from "effect"

Effect.runPromise(Effect.fail("my error")).catch(console.error) // Output: // (FiberFailure) Error: my error

@since2.0.0

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

Transforms an Effect into one that encapsulates both success and failure using the Either data type.

Details

either takes an effect that could potentially fail and converts it into an effect that always succeeds but with the result inside an Either. The Either can either be a Left (representing failure) or a Right (representing success). This allows you to handle both cases explicitly without causing the effect to fail.

The resulting effect cannot fail because failure is now represented inside the Either type.

@seeoption for a version that uses Option instead.

@seeexit for a version that uses Exit instead.

@example

import { Effect, Either, Random } from "effect"
class HttpError {
readonly _tag = "HttpError"
}
class ValidationError {
readonly _tag = "ValidationError"
}
// ┌─── Effect<string, HttpError | ValidationError, never>
// ▼
const program = Effect.gen(function* () {
const n1 = yield* Random.next
const n2 = yield* Random.next
if (n1 < 0.5) {
yield* Effect.fail(new HttpError())
}
if (n2 < 0.5) {
yield* Effect.fail(new ValidationError())
}
return "some result"
})
// ┌─── Effect<string, never, never>
// ▼
const recovered = Effect.gen(function* () {
// ┌─── Either<string, HttpError | ValidationError>
// ▼
const failureOrSuccess = yield* Effect.either(program)
return Either.match(failureOrSuccess, {
onLeft: (error) => `Recovering from ${error._tag}`,
onRight: (value) => value // Do nothing in case of success
})
})

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