Skip to content

Ref

When we write programs, it is common to need to keep track of some form of state during the execution of the program. State refers to any data that can change as the program runs. For example, in a counter application, the count value changes as the user increments or decrements it. Similarly, in a banking application, the account balance changes as deposits and withdrawals are made. State management is crucial to building interactive and dynamic applications.

In traditional imperative programming, one common way to store state is using variables. However, this approach can introduce bugs, especially when the state is shared between multiple components or functions. As the program becomes more complex, managing shared state can become challenging.

To overcome these issues, Effect introduces a powerful data type called Ref, which represents a mutable reference. With Ref, we can share state between different parts of our program without relying on mutable variables directly. Instead, Ref provides a controlled way to handle mutable state and safely update it in a concurrent environment.

Effect’s Ref data type enables communication between different fibers in your program. This capability is crucial in concurrent programming, where multiple tasks may need to access and update shared state simultaneously.

In this guide, we will explore how to use the Ref data type to manage state in your programs effectively. We will cover simple examples like counting, as well as more complex scenarios where state is shared between different parts of the program. Additionally, we will show how to use Ref in a concurrent environment, allowing multiple tasks to interact with shared state safely.

Let’s dive in and see how we can leverage Ref for effective state management in your Effect programs.

Here is a simple example using Ref to create a counter:

Example (Basic Counter with Ref)

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Ref
Ref
} from "effect"
class
class Counter
Counter
{
Counter.inc: Effect.Effect<void, never, never>
inc
:
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>
Counter.dec: Effect.Effect<void, never, never>
dec
:
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>
Counter.get: Effect.Effect<number, never, never>
get
:
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
<number>
constructor(private
Counter.value: Ref.Ref<number>
value
:
import Ref
Ref
.
interface Ref<in out A>

@since2.0.0

@since2.0.0

Ref
<number>) {
this.
Counter.inc: Effect.Effect<void, never, never>
inc
=
import Ref
Ref
.
const update: <number>(self: Ref.Ref<number>, f: (a: number) => number) => Effect.Effect<void> (+1 overload)

@since2.0.0

update
(this.
Counter.value: Ref.Ref<number>
value
, (
n: number
n
) =>
n: number
n
+ 1)
this.
Counter.dec: Effect.Effect<void, never, never>
dec
=
import Ref
Ref
.
const update: <number>(self: Ref.Ref<number>, f: (a: number) => number) => Effect.Effect<void> (+1 overload)

@since2.0.0

update
(this.
Counter.value: Ref.Ref<number>
value
, (
n: number
n
) =>
n: number
n
- 1)
this.
Counter.get: Effect.Effect<number, never, never>
get
=
import Ref
Ref
.
const get: <number>(self: Ref.Ref<number>) => Effect.Effect<number, never, never>

@since2.0.0

get
(this.
Counter.value: Ref.Ref<number>
value
)
}
}
const
const make: Effect.Effect<Counter, never, never>
make
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const andThen: <Ref.Ref<number>, never, never, Counter>(self: Effect.Effect<Ref.Ref<number>, never, never>, f: (a: Ref.Ref<number>) => Counter) => Effect.Effect<Counter, never, never> (+3 overloads)

Chains two actions, where the second action can depend on the result of the first.

Syntax

const transformedEffect = pipe(myEffect, Effect.andThen(anotherEffect))
// or
const transformedEffect = Effect.andThen(myEffect, anotherEffect)
// or
const transformedEffect = myEffect.pipe(Effect.andThen(anotherEffect))

When to Use

Use andThen when you need to run multiple actions in sequence, with the second action depending on the result of the first. This is useful for combining effects or handling computations that must happen in order.

Details

The second action can be:

  • A constant value (similar to

as

)

  • A function returning a value (similar to

map

)

  • A Promise
  • A function returning a Promise
  • An Effect
  • A function returning an Effect (similar to

flatMap

)

Note: andThen works well with both Option and Either types, treating them as effects.

@example

// Title: Applying a Discount Based on Fetched Amount
import { pipe, Effect } from "effect"
// Function to apply a discount safely to a transaction amount
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)
// Simulated asynchronous task to fetch a transaction amount from database
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
// Using Effect.map and Effect.flatMap
const result1 = pipe(
fetchTransactionAmount,
Effect.map((amount) => amount * 2),
Effect.flatMap((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result1).then(console.log)
// Output: 190
// Using Effect.andThen
const result2 = pipe(
fetchTransactionAmount,
Effect.andThen((amount) => amount * 2),
Effect.andThen((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result2).then(console.log)
// Output: 190

@since2.0.0

andThen
(
import Ref
Ref
.
const make: <number>(value: number) => Effect.Effect<Ref.Ref<number>, never, never>

@since2.0.0

make
(0), (
value: Ref.Ref<number>
value
) => new
constructor Counter(value: Ref.Ref<number>): Counter
Counter
(
value: Ref.Ref<number>
value
))

Example (Using the Counter)

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Ref
Ref
} from "effect"
13 collapsed lines
class
class Counter
Counter
{
Counter.inc: Effect.Effect<void, never, never>
inc
:
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>
Counter.dec: Effect.Effect<void, never, never>
dec
:
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>
Counter.get: Effect.Effect<number, never, never>
get
:
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
<number>
constructor(private
Counter.value: Ref.Ref<number>
value
:
import Ref
Ref
.
interface Ref<in out A>

@since2.0.0

@since2.0.0

Ref
<number>) {
this.
Counter.inc: Effect.Effect<void, never, never>
inc
=
import Ref
Ref
.
const update: <number>(self: Ref.Ref<number>, f: (a: number) => number) => Effect.Effect<void> (+1 overload)

@since2.0.0

update
(this.
Counter.value: Ref.Ref<number>
value
, (
n: number
n
) =>
n: number
n
+ 1)
this.
Counter.dec: Effect.Effect<void, never, never>
dec
=
import Ref
Ref
.
const update: <number>(self: Ref.Ref<number>, f: (a: number) => number) => Effect.Effect<void> (+1 overload)

@since2.0.0

update
(this.
Counter.value: Ref.Ref<number>
value
, (
n: number
n
) =>
n: number
n
- 1)
this.
Counter.get: Effect.Effect<number, never, never>
get
=
import Ref
Ref
.
const get: <number>(self: Ref.Ref<number>) => Effect.Effect<number, never, never>

@since2.0.0

get
(this.
Counter.value: Ref.Ref<number>
value
)
}
}
const
const make: Effect.Effect<Counter, never, never>
make
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const andThen: <Ref.Ref<number>, never, never, Counter>(self: Effect.Effect<Ref.Ref<number>, never, never>, f: (a: Ref.Ref<number>) => Counter) => Effect.Effect<Counter, never, never> (+3 overloads)

Chains two actions, where the second action can depend on the result of the first.

Syntax

const transformedEffect = pipe(myEffect, Effect.andThen(anotherEffect))
// or
const transformedEffect = Effect.andThen(myEffect, anotherEffect)
// or
const transformedEffect = myEffect.pipe(Effect.andThen(anotherEffect))

When to Use

Use andThen when you need to run multiple actions in sequence, with the second action depending on the result of the first. This is useful for combining effects or handling computations that must happen in order.

Details

The second action can be:

  • A constant value (similar to

as

)

  • A function returning a value (similar to

map

)

  • A Promise
  • A function returning a Promise
  • An Effect
  • A function returning an Effect (similar to

flatMap

)

Note: andThen works well with both Option and Either types, treating them as effects.

@example

// Title: Applying a Discount Based on Fetched Amount
import { pipe, Effect } from "effect"
// Function to apply a discount safely to a transaction amount
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)
// Simulated asynchronous task to fetch a transaction amount from database
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
// Using Effect.map and Effect.flatMap
const result1 = pipe(
fetchTransactionAmount,
Effect.map((amount) => amount * 2),
Effect.flatMap((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result1).then(console.log)
// Output: 190
// Using Effect.andThen
const result2 = pipe(
fetchTransactionAmount,
Effect.andThen((amount) => amount * 2),
Effect.andThen((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result2).then(console.log)
// Output: 190

@since2.0.0

andThen
(
import Ref
Ref
.
const make: <number>(value: number) => Effect.Effect<Ref.Ref<number>, never, never>

@since2.0.0

make
(0), (
value: Ref.Ref<number>
value
) => new
constructor Counter(value: Ref.Ref<number>): Counter
Counter
(
value: Ref.Ref<number>
value
))
const
const program: Effect.Effect<void, never, never>
program
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<void, never, never>>, void>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<void, never, never>>, void, never>) => Effect.Effect<...> (+1 overload)

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

When to Use

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 counter: Counter
counter
= yield*
const make: Effect.Effect<Counter, never, never>
make
yield*
const counter: Counter
counter
.
Counter.inc: Effect.Effect<void, never, never>
inc
yield*
const counter: Counter
counter
.
Counter.inc: Effect.Effect<void, never, never>
inc
yield*
const counter: Counter
counter
.
Counter.dec: Effect.Effect<void, never, never>
dec
yield*
const counter: Counter
counter
.
Counter.inc: Effect.Effect<void, never, never>
inc
const
const value: number
value
= yield*
const counter: Counter
counter
.
Counter.get: Effect.Effect<number, never, never>
get
var console: Console

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

The module exports two specific components:

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

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

Example using the global console:

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

Example using the Console class:

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

@seesource

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

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

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

See util.format() for more information.

@sincev0.1.100

log
(`This counter has a value of ${
const value: number
value
}.`)
})
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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

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
(
const program: Effect.Effect<void, never, never>
program
)
/*
Output:
This counter has a value of 2.
*/

We can also use Ref in concurrent scenarios, where multiple tasks might be updating shared state at the same time.

Example (Concurrent Updates to Shared Counter)

For this example, let’s update the counter concurrently:

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Ref
Ref
} from "effect"
13 collapsed lines
class
class Counter
Counter
{
Counter.inc: Effect.Effect<void, never, never>
inc
:
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>
Counter.dec: Effect.Effect<void, never, never>
dec
:
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>
Counter.get: Effect.Effect<number, never, never>
get
:
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
<number>
constructor(private
Counter.value: Ref.Ref<number>
value
:
import Ref
Ref
.
interface Ref<in out A>

@since2.0.0

@since2.0.0

Ref
<number>) {
this.
Counter.inc: Effect.Effect<void, never, never>
inc
=
import Ref
Ref
.
const update: <number>(self: Ref.Ref<number>, f: (a: number) => number) => Effect.Effect<void> (+1 overload)

@since2.0.0

update
(this.
Counter.value: Ref.Ref<number>
value
, (
n: number
n
) =>
n: number
n
+ 1)
this.
Counter.dec: Effect.Effect<void, never, never>
dec
=
import Ref
Ref
.
const update: <number>(self: Ref.Ref<number>, f: (a: number) => number) => Effect.Effect<void> (+1 overload)

@since2.0.0

update
(this.
Counter.value: Ref.Ref<number>
value
, (
n: number
n
) =>
n: number
n
- 1)
this.
Counter.get: Effect.Effect<number, never, never>
get
=
import Ref
Ref
.
const get: <number>(self: Ref.Ref<number>) => Effect.Effect<number, never, never>

@since2.0.0

get
(this.
Counter.value: Ref.Ref<number>
value
)
}
}
const
const make: Effect.Effect<Counter, never, never>
make
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const andThen: <Ref.Ref<number>, never, never, Counter>(self: Effect.Effect<Ref.Ref<number>, never, never>, f: (a: Ref.Ref<number>) => Counter) => Effect.Effect<Counter, never, never> (+3 overloads)

Chains two actions, where the second action can depend on the result of the first.

Syntax

const transformedEffect = pipe(myEffect, Effect.andThen(anotherEffect))
// or
const transformedEffect = Effect.andThen(myEffect, anotherEffect)
// or
const transformedEffect = myEffect.pipe(Effect.andThen(anotherEffect))

When to Use

Use andThen when you need to run multiple actions in sequence, with the second action depending on the result of the first. This is useful for combining effects or handling computations that must happen in order.

Details

The second action can be:

  • A constant value (similar to

as

)

  • A function returning a value (similar to

map

)

  • A Promise
  • A function returning a Promise
  • An Effect
  • A function returning an Effect (similar to

flatMap

)

Note: andThen works well with both Option and Either types, treating them as effects.

@example

// Title: Applying a Discount Based on Fetched Amount
import { pipe, Effect } from "effect"
// Function to apply a discount safely to a transaction amount
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)
// Simulated asynchronous task to fetch a transaction amount from database
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
// Using Effect.map and Effect.flatMap
const result1 = pipe(
fetchTransactionAmount,
Effect.map((amount) => amount * 2),
Effect.flatMap((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result1).then(console.log)
// Output: 190
// Using Effect.andThen
const result2 = pipe(
fetchTransactionAmount,
Effect.andThen((amount) => amount * 2),
Effect.andThen((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result2).then(console.log)
// Output: 190

@since2.0.0

andThen
(
import Ref
Ref
.
const make: <number>(value: number) => Effect.Effect<Ref.Ref<number>, never, never>

@since2.0.0

make
(0), (
value: Ref.Ref<number>
value
) => new
constructor Counter(value: Ref.Ref<number>): Counter
Counter
(
value: Ref.Ref<number>
value
))
const
const program: Effect.Effect<void, never, never>
program
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<void, never, never>>, void>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<void, never, never>>, void, never>) => Effect.Effect<...> (+1 overload)

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

When to Use

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 counter: Counter
counter
= yield*
const make: Effect.Effect<Counter, never, never>
make
// Helper to log the counter's value before running an effect
const
const logCounter: <R, E, A>(label: string, effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>
logCounter
= <
function (type parameter) R in <R, E, A>(label: string, effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
R
,
function (type parameter) E in <R, E, A>(label: string, effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
E
,
function (type parameter) A in <R, E, A>(label: string, effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
A
>(
label: string
label
: string,
effect: Effect.Effect<A, E, R>
effect
:
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
<
function (type parameter) A in <R, E, A>(label: string, effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
A
,
function (type parameter) E in <R, E, A>(label: string, effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
E
,
function (type parameter) R in <R, E, A>(label: string, effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
R
>
) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<A, E, R>> | YieldWrap<Effect.Effect<void, never, never>>, A>(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 value: number
value
= yield*
const counter: Counter
counter
.
Counter.get: Effect.Effect<number, never, never>
get
yield*
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const log: (...message: ReadonlyArray<any>) => Effect.Effect<void, never, never>

Logs one or more messages or error causes at the current log level, which is INFO by default. This function allows logging multiple items at once and can include detailed error information using Cause instances.

To adjust the log level, use the Logger.withMinimumLogLevel function.

@example

import { Cause, Effect } from "effect"
const program = Effect.log(
"message1",
"message2",
Cause.die("Oh no!"),
Cause.die("Oh uh!")
)
// Effect.runFork(program)
// Output:
// timestamp=... level=INFO fiber=#0 message=message1 message=message2 cause="Error: Oh no!
// Error: Oh uh!"

@since2.0.0

log
(`${
label: string
label
} get: ${
const value: number
value
}`)
return yield*
effect: Effect.Effect<A, E, R>
effect
})
yield*
const logCounter: <never, never, void>(label: string, effect: Effect.Effect<void, never, never>) => Effect.Effect<void, never, never>
logCounter
("task 1",
const counter: Counter
counter
.
Counter.inc: Effect.Effect<void, never, never>
inc
).
Pipeable.pipe<Effect.Effect<void, never, never>, Effect.Effect<[void, void], never, never>, Effect.Effect<[[void, void], void], never, never>, Effect.Effect<[[[void, void], void], void], never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<...>) => Effect.Effect<...>, bc: (_: Effect.Effect<...>) => Effect.Effect<...>, cd: (_: Effect.Effect<...>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const zip: <void, never, never>(that: Effect.Effect<void, never, never>, options?: {
readonly concurrent?: boolean | undefined;
readonly batching?: boolean | "inherit" | undefined;
readonly concurrentFinalizers?: boolean | undefined;
} | undefined) => <A, E, R>(self: Effect.Effect<...>) => Effect.Effect<...> (+1 overload)

Combines two effects into a single effect, producing a tuple with the results of both effects.

The zip function executes the first effect (left) and then the second effect (right). Once both effects succeed, their results are combined into a tuple.

Concurrency

By default, zip processes the effects sequentially. To execute the effects concurrently, use the { concurrent: true } option.

@seezipWith for a version that combines the results with a custom function.

@seevalidate for a version that accumulates errors.

@example

// Title: Combining Two Effects Sequentially
import { Effect } from "effect"
const task1 = Effect.succeed(1).pipe(
Effect.delay("200 millis"),
Effect.tap(Effect.log("task1 done"))
)
const task2 = Effect.succeed("hello").pipe(
Effect.delay("100 millis"),
Effect.tap(Effect.log("task2 done"))
)
// Combine the two effects together
//
// ┌─── Effect<[number, string], never, never>
// ▼
const program = Effect.zip(task1, task2)
Effect.runPromise(program).then(console.log)
// Output:
// timestamp=... level=INFO fiber=#0 message="task1 done"
// timestamp=... level=INFO fiber=#0 message="task2 done"
// [ 1, 'hello' ]

@example

// Title: Combining Two Effects Concurrently import { Effect } from "effect"

const task1 = Effect.succeed(1).pipe( Effect.delay("200 millis"), Effect.tap(Effect.log("task1 done")) ) const task2 = Effect.succeed("hello").pipe( Effect.delay("100 millis"), Effect.tap(Effect.log("task2 done")) )

// Run both effects concurrently using the concurrent option const program = Effect.zip(task1, task2, { concurrent: true })

Effect.runPromise(program).then(console.log) // Output: // timestamp=... level=INFO fiber=#0 message="task2 done" // timestamp=... level=INFO fiber=#0 message="task1 done" // [ 1, 'hello' ]

@since2.0.0

zip
(
const logCounter: <never, never, void>(label: string, effect: Effect.Effect<void, never, never>) => Effect.Effect<void, never, never>
logCounter
("task 2",
const counter: Counter
counter
.
Counter.inc: Effect.Effect<void, never, never>
inc
), {
concurrent?: boolean | undefined
concurrent
: true }),
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const zip: <void, never, never>(that: Effect.Effect<void, never, never>, options?: {
readonly concurrent?: boolean | undefined;
readonly batching?: boolean | "inherit" | undefined;
readonly concurrentFinalizers?: boolean | undefined;
} | undefined) => <A, E, R>(self: Effect.Effect<...>) => Effect.Effect<...> (+1 overload)

Combines two effects into a single effect, producing a tuple with the results of both effects.

The zip function executes the first effect (left) and then the second effect (right). Once both effects succeed, their results are combined into a tuple.

Concurrency

By default, zip processes the effects sequentially. To execute the effects concurrently, use the { concurrent: true } option.

@seezipWith for a version that combines the results with a custom function.

@seevalidate for a version that accumulates errors.

@example

// Title: Combining Two Effects Sequentially
import { Effect } from "effect"
const task1 = Effect.succeed(1).pipe(
Effect.delay("200 millis"),
Effect.tap(Effect.log("task1 done"))
)
const task2 = Effect.succeed("hello").pipe(
Effect.delay("100 millis"),
Effect.tap(Effect.log("task2 done"))
)
// Combine the two effects together
//
// ┌─── Effect<[number, string], never, never>
// ▼
const program = Effect.zip(task1, task2)
Effect.runPromise(program).then(console.log)
// Output:
// timestamp=... level=INFO fiber=#0 message="task1 done"
// timestamp=... level=INFO fiber=#0 message="task2 done"
// [ 1, 'hello' ]

@example

// Title: Combining Two Effects Concurrently import { Effect } from "effect"

const task1 = Effect.succeed(1).pipe( Effect.delay("200 millis"), Effect.tap(Effect.log("task1 done")) ) const task2 = Effect.succeed("hello").pipe( Effect.delay("100 millis"), Effect.tap(Effect.log("task2 done")) )

// Run both effects concurrently using the concurrent option const program = Effect.zip(task1, task2, { concurrent: true })

Effect.runPromise(program).then(console.log) // Output: // timestamp=... level=INFO fiber=#0 message="task2 done" // timestamp=... level=INFO fiber=#0 message="task1 done" // [ 1, 'hello' ]

@since2.0.0

zip
(
const logCounter: <never, never, void>(label: string, effect: Effect.Effect<void, never, never>) => Effect.Effect<void, never, never>
logCounter
("task 3",
const counter: Counter
counter
.
Counter.dec: Effect.Effect<void, never, never>
dec
), {
concurrent?: boolean | undefined
concurrent
: true }),
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const zip: <void, never, never>(that: Effect.Effect<void, never, never>, options?: {
readonly concurrent?: boolean | undefined;
readonly batching?: boolean | "inherit" | undefined;
readonly concurrentFinalizers?: boolean | undefined;
} | undefined) => <A, E, R>(self: Effect.Effect<...>) => Effect.Effect<...> (+1 overload)

Combines two effects into a single effect, producing a tuple with the results of both effects.

The zip function executes the first effect (left) and then the second effect (right). Once both effects succeed, their results are combined into a tuple.

Concurrency

By default, zip processes the effects sequentially. To execute the effects concurrently, use the { concurrent: true } option.

@seezipWith for a version that combines the results with a custom function.

@seevalidate for a version that accumulates errors.

@example

// Title: Combining Two Effects Sequentially
import { Effect } from "effect"
const task1 = Effect.succeed(1).pipe(
Effect.delay("200 millis"),
Effect.tap(Effect.log("task1 done"))
)
const task2 = Effect.succeed("hello").pipe(
Effect.delay("100 millis"),
Effect.tap(Effect.log("task2 done"))
)
// Combine the two effects together
//
// ┌─── Effect<[number, string], never, never>
// ▼
const program = Effect.zip(task1, task2)
Effect.runPromise(program).then(console.log)
// Output:
// timestamp=... level=INFO fiber=#0 message="task1 done"
// timestamp=... level=INFO fiber=#0 message="task2 done"
// [ 1, 'hello' ]

@example

// Title: Combining Two Effects Concurrently import { Effect } from "effect"

const task1 = Effect.succeed(1).pipe( Effect.delay("200 millis"), Effect.tap(Effect.log("task1 done")) ) const task2 = Effect.succeed("hello").pipe( Effect.delay("100 millis"), Effect.tap(Effect.log("task2 done")) )

// Run both effects concurrently using the concurrent option const program = Effect.zip(task1, task2, { concurrent: true })

Effect.runPromise(program).then(console.log) // Output: // timestamp=... level=INFO fiber=#0 message="task2 done" // timestamp=... level=INFO fiber=#0 message="task1 done" // [ 1, 'hello' ]

@since2.0.0

zip
(
const logCounter: <never, never, void>(label: string, effect: Effect.Effect<void, never, never>) => Effect.Effect<void, never, never>
logCounter
("task 4",
const counter: Counter
counter
.
Counter.inc: Effect.Effect<void, never, never>
inc
), {
concurrent?: boolean | undefined
concurrent
: true })
)
const
const value: number
value
= yield*
const counter: Counter
counter
.
Counter.get: Effect.Effect<number, never, never>
get
yield*
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const log: (...message: ReadonlyArray<any>) => Effect.Effect<void, never, never>

Logs one or more messages or error causes at the current log level, which is INFO by default. This function allows logging multiple items at once and can include detailed error information using Cause instances.

To adjust the log level, use the Logger.withMinimumLogLevel function.

@example

import { Cause, Effect } from "effect"
const program = Effect.log(
"message1",
"message2",
Cause.die("Oh no!"),
Cause.die("Oh uh!")
)
// Effect.runFork(program)
// Output:
// timestamp=... level=INFO fiber=#0 message=message1 message=message2 cause="Error: Oh no!
// Error: Oh uh!"

@since2.0.0

log
(`This counter has a value of ${
const value: number
value
}.`)
})
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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

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
(
const program: Effect.Effect<void, never, never>
program
)
/*
Output:
timestamp=... fiber=#3 message="task 4 get: 0"
timestamp=... fiber=#6 message="task 3 get: 1"
timestamp=... fiber=#8 message="task 1 get: 0"
timestamp=... fiber=#9 message="task 2 get: 1"
timestamp=... fiber=#0 message="This counter has a value of 2."
*/

You can pass a Ref as a service to share state across different parts of your program.

Example (Using Ref as a Service)

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Context

@since2.0.0

@since2.0.0

Context
,
import Ref
Ref
} from "effect"
// Create a Tag for our state
class
class MyState
MyState
extends
import Context

@since2.0.0

@since2.0.0

Context
.
const Tag: <"MyState">(id: "MyState") => <Self, Shape>() => Context.TagClass<Self, "MyState", 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
("MyState")<
class MyState
MyState
,
import Ref
Ref
.
interface Ref<in out A>

@since2.0.0

@since2.0.0

Ref
<number>
>() {}
// Subprogram 1: Increment the state value twice
const
const subprogram1: Effect.Effect<void, never, MyState>
subprogram1
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Context.Tag<MyState, Ref.Ref<number>>> | YieldWrap<Effect.Effect<void, never, never>>, void>(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 state: Ref.Ref<number>
state
= yield*
class MyState
MyState
yield*
import Ref
Ref
.
const update: <number>(self: Ref.Ref<number>, f: (a: number) => number) => Effect.Effect<void> (+1 overload)

@since2.0.0

update
(
const state: Ref.Ref<number>
state
, (
n: number
n
) =>
n: number
n
+ 1)
yield*
import Ref
Ref
.
const update: <number>(self: Ref.Ref<number>, f: (a: number) => number) => Effect.Effect<void> (+1 overload)

@since2.0.0

update
(
const state: Ref.Ref<number>
state
, (
n: number
n
) =>
n: number
n
+ 1)
})
// Subprogram 2: Decrement the state value and then increment it
const
const subprogram2: Effect.Effect<void, never, MyState>
subprogram2
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Context.Tag<MyState, Ref.Ref<number>>> | YieldWrap<Effect.Effect<void, never, never>>, void>(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 state: Ref.Ref<number>
state
= yield*
class MyState
MyState
yield*
import Ref
Ref
.
const update: <number>(self: Ref.Ref<number>, f: (a: number) => number) => Effect.Effect<void> (+1 overload)

@since2.0.0

update
(
const state: Ref.Ref<number>
state
, (
n: number
n
) =>
n: number
n
- 1)
yield*
import Ref
Ref
.
const update: <number>(self: Ref.Ref<number>, f: (a: number) => number) => Effect.Effect<void> (+1 overload)

@since2.0.0

update
(
const state: Ref.Ref<number>
state
, (
n: number
n
) =>
n: number
n
+ 1)
})
// Subprogram 3: Read and log the current value of the state
const
const subprogram3: Effect.Effect<void, never, MyState>
subprogram3
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Context.Tag<MyState, Ref.Ref<number>>> | YieldWrap<Effect.Effect<number, never, never>>, void>(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 state: Ref.Ref<number>
state
= yield*
class MyState
MyState
const
const value: number
value
= yield*
import Ref
Ref
.
const get: <number>(self: Ref.Ref<number>) => Effect.Effect<number, never, never>

@since2.0.0

get
(
const state: Ref.Ref<number>
state
)
var console: Console

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

The module exports two specific components:

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

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

Example using the global console:

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

Example using the Console class:

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

@seesource

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

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

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

See util.format() for more information.

@sincev0.1.100

log
(`MyState has a value of ${
const value: number
value
}.`)
})
// Compose subprograms 1, 2, and 3 to create the main program
const
const program: Effect.Effect<void, never, MyState>
program
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<void, never, MyState>>, void>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<void, never, MyState>>, void, never>) => Effect.Effect<...> (+1 overload)

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

When to Use

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* () {
yield*
const subprogram1: Effect.Effect<void, never, MyState>
subprogram1
yield*
const subprogram2: Effect.Effect<void, never, MyState>
subprogram2
yield*
const subprogram3: Effect.Effect<void, never, MyState>
subprogram3
})
// Create a Ref instance with an initial value of 0
const
const initialState: Effect.Effect<Ref.Ref<number>, never, never>
initialState
=
import Ref
Ref
.
const make: <number>(value: number) => Effect.Effect<Ref.Ref<number>, never, never>

@since2.0.0

make
(0)
// Provide the Ref as a service
const
const runnable: Effect.Effect<void, never, never>
runnable
=
const program: Effect.Effect<void, never, MyState>
program
.
Pipeable.pipe<Effect.Effect<void, never, MyState>, Effect.Effect<void, never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<void, never, MyState>) => Effect.Effect<void, never, never>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const provideServiceEffect: <typeof MyState, never, never>(tag: typeof MyState, effect: Effect.Effect<Ref.Ref<number>, never, never>) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<...> (+1 overload)

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

@since2.0.0

provideServiceEffect
(
class MyState
MyState
,
const initialState: Effect.Effect<Ref.Ref<number>, never, never>
initialState
)
)
// Run the program and observe the output
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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

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
(
const runnable: Effect.Effect<void, never, never>
runnable
)
/*
Output:
MyState has a value of 2.
*/

Note that we use Effect.provideServiceEffect instead of Effect.provideService to provide an actual implementation of the MyState service because all the operations on the Ref data type are effectful, including the creation Ref.make(0).

You can use Ref to manage shared state between multiple fibers in a concurrent environment.

Example (Managing Shared State Across Fibers)

Let’s look at an example where we continuously read names from user input until the user enters "q" to exit.

First, let’s introduce a readLine utility to read user input (ensure you have @types/node installed):

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
} from "effect"
import * as
module "node:readline"
NodeReadLine
from "node:readline"
// Utility to read user input
const
const readLine: (message: string) => Effect.Effect<string>
readLine
= (
message: string
message
: string):
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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

The Effect interface defines a value that 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
<string> =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const promise: <string>(evaluate: (signal: AbortSignal) => PromiseLike<string>) => Effect.Effect<string, never, never>

Creates an Effect that represents an asynchronous computation guaranteed to succeed.

When to Use

Use promise when you are sure the operation will not reject.

Details

The provided function (thunk) returns a Promise that should never reject; if it does, the error will be treated as a "defect".

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

catchAllDefect

.

Interruptions

An optional AbortSignal can be provided to allow for interruption of the wrapped Promise API.

@seetryPromise for a version that can handle failures.

@example

// Title: Delayed Message
import { Effect } from "effect"
const delay = (message: string) =>
Effect.promise<string>(
() =>
new Promise((resolve) => {
setTimeout(() => {
resolve(message)
}, 2000)
})
)
// ┌─── Effect<string, never, never>
// ▼
const program = delay("Async operation completed successfully!")

@since2.0.0

promise
(
() =>
new
var Promise: PromiseConstructor
new <string>(executor: (resolve: (value: string | PromiseLike<string>) => void, reject: (reason?: any) => void) => void) => Promise<string>

Creates a new Promise.

@paramexecutor A callback used to initialize the promise. This callback is passed two arguments: a resolve callback used to resolve the promise with a value or the result of another promise, and a reject callback used to reject the promise with a provided reason or error.

Promise
((
resolve: (value: string | PromiseLike<string>) => void
resolve
) => {
const
const rl: NodeReadLine.Interface
rl
=
module "node:readline"
NodeReadLine
.
function createInterface(options: NodeReadLine.ReadLineOptions): NodeReadLine.Interface (+1 overload)

The readline.createInterface() method creates a new readline.Interface instance.

import readline from 'node:readline';
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});

Once the readline.Interface instance is created, the most common case is to listen for the 'line' event:

rl.on('line', (line) => {
console.log(`Received: ${line}`);
});

If terminal is true for this instance then the output stream will get the best compatibility if it defines an output.columns property and emits a 'resize' event on the output if or when the columns ever change (process.stdout does this automatically when it is a TTY).

When creating a readline.Interface using stdin as input, the program will not terminate until it receives an EOF character. To exit without waiting for user input, call process.stdin.unref().

@sincev0.1.98

createInterface
({
ReadLineOptions.input: NodeJS.ReadableStream

The Readable stream to listen to

input
:
var process: NodeJS.Process
process
.
NodeJS.Process.stdin: NodeJS.ReadStream & {
fd: 0;
}

The process.stdin property returns a stream connected tostdin (fd 0). It is a net.Socket (which is a Duplex stream) unless fd 0 refers to a file, in which case it is a Readable stream.

For details of how to read from stdin see readable.read().

As a Duplex stream, process.stdin can also be used in "old" mode that is compatible with scripts written for Node.js prior to v0.10. For more information see Stream compatibility.

In "old" streams mode the stdin stream is paused by default, so one must call process.stdin.resume() to read from it. Note also that calling process.stdin.resume() itself would switch stream to "old" mode.

stdin
,
ReadLineOptions.output?: NodeJS.WritableStream | undefined

The Writable stream to write readline data to.

output
:
var process: NodeJS.Process
process
.
NodeJS.Process.stdout: NodeJS.WriteStream & {
fd: 1;
}

The process.stdout property returns a stream connected tostdout (fd 1). It is a net.Socket (which is a Duplex stream) unless fd 1 refers to a file, in which case it is a Writable stream.

For example, to copy process.stdin to process.stdout:

import { stdin, stdout } from 'node:process';
stdin.pipe(stdout);

process.stdout differs from other Node.js streams in important ways. See note on process I/O for more information.

stdout
})
const rl: NodeReadLine.Interface
rl
.
Interface.question(query: string, callback: (answer: string) => void): void (+1 overload)

The rl.question() method displays the query by writing it to the output, waits for user input to be provided on input, then invokes the callback function passing the provided input as the first argument.

When called, rl.question() will resume the input stream if it has been paused.

If the Interface was created with output set to null or undefined the query is not written.

The callback function passed to rl.question() does not follow the typical pattern of accepting an Error object or null as the first argument. The callback is called with the provided answer as the only argument.

An error will be thrown if calling rl.question() after rl.close().

Example usage:

rl.question('What is your favorite food? ', (answer) => {
console.log(`Oh, so your favorite food is ${answer}`);
});

Using an AbortController to cancel a question.

const ac = new AbortController();
const signal = ac.signal;
rl.question('What is your favorite food? ', { signal }, (answer) => {
console.log(`Oh, so your favorite food is ${answer}`);
});
signal.addEventListener('abort', () => {
console.log('The food question timed out');
}, { once: true });
setTimeout(() => ac.abort(), 10000);

@sincev0.3.3

@paramquery A statement or query to write to output, prepended to the prompt.

@paramcallback A callback function that is invoked with the user's input in response to the query.

question
(
message: string
message
, (
answer: string
answer
) => {
const rl: NodeReadLine.Interface
rl
.
Interface.close(): void

The rl.close() method closes the Interface instance and relinquishes control over the input and output streams. When called, the 'close' event will be emitted.

Calling rl.close() does not immediately stop other events (including 'line') from being emitted by the Interface instance.

@sincev0.1.98

close
()
resolve: (value: string | PromiseLike<string>) => void
resolve
(
answer: string
answer
)
})
})
)

Next, we implement the main program to collect names:

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Chunk
Chunk
,
import Ref
Ref
} from "effect"
import * as
module "node:readline"
NodeReadLine
from "node:readline"
// Utility to read user input
14 collapsed lines
const
const readLine: (message: string) => Effect.Effect<string>
readLine
= (
message: string
message
: string):
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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

The Effect interface defines a value that 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
<string> =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const promise: <string>(evaluate: (signal: AbortSignal) => PromiseLike<string>) => Effect.Effect<string, never, never>

Creates an Effect that represents an asynchronous computation guaranteed to succeed.

When to Use

Use promise when you are sure the operation will not reject.

Details

The provided function (thunk) returns a Promise that should never reject; if it does, the error will be treated as a "defect".

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

catchAllDefect

.

Interruptions

An optional AbortSignal can be provided to allow for interruption of the wrapped Promise API.

@seetryPromise for a version that can handle failures.

@example

// Title: Delayed Message
import { Effect } from "effect"
const delay = (message: string) =>
Effect.promise<string>(
() =>
new Promise((resolve) => {
setTimeout(() => {
resolve(message)
}, 2000)
})
)
// ┌─── Effect<string, never, never>
// ▼
const program = delay("Async operation completed successfully!")

@since2.0.0

promise
(
() =>
new
var Promise: PromiseConstructor
new <string>(executor: (resolve: (value: string | PromiseLike<string>) => void, reject: (reason?: any) => void) => void) => Promise<string>

Creates a new Promise.

@paramexecutor A callback used to initialize the promise. This callback is passed two arguments: a resolve callback used to resolve the promise with a value or the result of another promise, and a reject callback used to reject the promise with a provided reason or error.

Promise
((
resolve: (value: string | PromiseLike<string>) => void
resolve
) => {
const
const rl: NodeReadLine.Interface
rl
=
module "node:readline"
NodeReadLine
.
function createInterface(options: NodeReadLine.ReadLineOptions): NodeReadLine.Interface (+1 overload)

The readline.createInterface() method creates a new readline.Interface instance.

import readline from 'node:readline';
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});

Once the readline.Interface instance is created, the most common case is to listen for the 'line' event:

rl.on('line', (line) => {
console.log(`Received: ${line}`);
});

If terminal is true for this instance then the output stream will get the best compatibility if it defines an output.columns property and emits a 'resize' event on the output if or when the columns ever change (process.stdout does this automatically when it is a TTY).

When creating a readline.Interface using stdin as input, the program will not terminate until it receives an EOF character. To exit without waiting for user input, call process.stdin.unref().

@sincev0.1.98

createInterface
({
ReadLineOptions.input: NodeJS.ReadableStream

The Readable stream to listen to

input
:
var process: NodeJS.Process
process
.
NodeJS.Process.stdin: NodeJS.ReadStream & {
fd: 0;
}

The process.stdin property returns a stream connected tostdin (fd 0). It is a net.Socket (which is a Duplex stream) unless fd 0 refers to a file, in which case it is a Readable stream.

For details of how to read from stdin see readable.read().

As a Duplex stream, process.stdin can also be used in "old" mode that is compatible with scripts written for Node.js prior to v0.10. For more information see Stream compatibility.

In "old" streams mode the stdin stream is paused by default, so one must call process.stdin.resume() to read from it. Note also that calling process.stdin.resume() itself would switch stream to "old" mode.

stdin
,
ReadLineOptions.output?: NodeJS.WritableStream | undefined

The Writable stream to write readline data to.

output
:
var process: NodeJS.Process
process
.
NodeJS.Process.stdout: NodeJS.WriteStream & {
fd: 1;
}

The process.stdout property returns a stream connected tostdout (fd 1). It is a net.Socket (which is a Duplex stream) unless fd 1 refers to a file, in which case it is a Writable stream.

For example, to copy process.stdin to process.stdout:

import { stdin, stdout } from 'node:process';
stdin.pipe(stdout);

process.stdout differs from other Node.js streams in important ways. See note on process I/O for more information.

stdout
})
const rl: NodeReadLine.Interface
rl
.
Interface.question(query: string, callback: (answer: string) => void): void (+1 overload)

The rl.question() method displays the query by writing it to the output, waits for user input to be provided on input, then invokes the callback function passing the provided input as the first argument.

When called, rl.question() will resume the input stream if it has been paused.

If the Interface was created with output set to null or undefined the query is not written.

The callback function passed to rl.question() does not follow the typical pattern of accepting an Error object or null as the first argument. The callback is called with the provided answer as the only argument.

An error will be thrown if calling rl.question() after rl.close().

Example usage:

rl.question('What is your favorite food? ', (answer) => {
console.log(`Oh, so your favorite food is ${answer}`);
});

Using an AbortController to cancel a question.

const ac = new AbortController();
const signal = ac.signal;
rl.question('What is your favorite food? ', { signal }, (answer) => {
console.log(`Oh, so your favorite food is ${answer}`);
});
signal.addEventListener('abort', () => {
console.log('The food question timed out');
}, { once: true });
setTimeout(() => ac.abort(), 10000);

@sincev0.3.3

@paramquery A statement or query to write to output, prepended to the prompt.

@paramcallback A callback function that is invoked with the user's input in response to the query.

question
(
message: string
message
, (
answer: string
answer
) => {
const rl: NodeReadLine.Interface
rl
.
Interface.close(): void

The rl.close() method closes the Interface instance and relinquishes control over the input and output streams. When called, the 'close' event will be emitted.

Calling rl.close() does not immediately stop other events (including 'line') from being emitted by the Interface instance.

@sincev0.1.98

close
()
resolve: (value: string | PromiseLike<string>) => void
resolve
(
answer: string
answer
)
})
})
)
const
const getNames: Effect.Effect<Chunk.Chunk<string>, never, never>
getNames
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<void, never, never>>, Chunk.Chunk<string>>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<void, never, never>>, Chunk.Chunk<...>, 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* () {
const
const ref: Ref.Ref<Chunk.Chunk<string>>
ref
= yield*
import Ref
Ref
.
const make: <Chunk.Chunk<string>>(value: Chunk.Chunk<string>) => Effect.Effect<Ref.Ref<Chunk.Chunk<string>>, never, never>

@since2.0.0

make
(
import Chunk
Chunk
.
const empty: <string>() => Chunk.Chunk<string>

@since2.0.0

empty
<string>())
while (true) {
const
const name: string
name
= yield*
const readLine: (message: string) => Effect.Effect<string>
readLine
("Please enter a name or `q` to exit: ")
if (
const name: string
name
=== "q") {
break
}
yield*
import Ref
Ref
.
const update: <Chunk.Chunk<string>>(self: Ref.Ref<Chunk.Chunk<string>>, f: (a: Chunk.Chunk<string>) => Chunk.Chunk<string>) => Effect.Effect<void> (+1 overload)

@since2.0.0

update
(
const ref: Ref.Ref<Chunk.Chunk<string>>
ref
, (
state: Chunk.Chunk<string>
state
) =>
import Chunk
Chunk
.
const append: <string, string>(self: Chunk.Chunk<string>, a: string) => Chunk.NonEmptyChunk<string> (+1 overload)

Appends the specified element to the end of the Chunk.

@since2.0.0

append
(
state: Chunk.Chunk<string>
state
,
const name: string
name
))
}
return yield*
import Ref
Ref
.
const get: <Chunk.Chunk<string>>(self: Ref.Ref<Chunk.Chunk<string>>) => Effect.Effect<Chunk.Chunk<string>, never, never>

@since2.0.0

get
(
const ref: Ref.Ref<Chunk.Chunk<string>>
ref
)
})
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const runPromise: <Chunk.Chunk<string>, never>(effect: Effect.Effect<Chunk.Chunk<string>, 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
(
const getNames: Effect.Effect<Chunk.Chunk<string>, never, never>
getNames
).
Promise<Chunk<string>>.then<void, never>(onfulfilled?: ((value: Chunk.Chunk<string>) => void | PromiseLike<void>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | 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
.
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
)
/*
Output:
Please enter a name or `q` to exit: Alice
Please enter a name or `q` to exit: Bob
Please enter a name or `q` to exit: q
{
_id: "Chunk",
values: [ "Alice", "Bob" ]
}
*/

Now that we have learned how to use the Ref data type, we can use it to manage the state concurrently.

For example, assume while we are reading from the console, we have another fiber that is trying to update the state from a different source.

Here, one fiber reads names from user input, while another fiber concurrently adds preset names at regular intervals:

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Chunk
Chunk
,
import Ref
Ref
,
import Fiber
Fiber
} from "effect"
import * as
module "node:readline"
NodeReadLine
from "node:readline"
// Utility to read user input
14 collapsed lines
const
const readLine: (message: string) => Effect.Effect<string>
readLine
= (
message: string
message
: string):
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

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

The Effect interface defines a value that 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
<string> =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const promise: <string>(evaluate: (signal: AbortSignal) => PromiseLike<string>) => Effect.Effect<string, never, never>

Creates an Effect that represents an asynchronous computation guaranteed to succeed.

When to Use

Use promise when you are sure the operation will not reject.

Details

The provided function (thunk) returns a Promise that should never reject; if it does, the error will be treated as a "defect".

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

catchAllDefect

.

Interruptions

An optional AbortSignal can be provided to allow for interruption of the wrapped Promise API.

@seetryPromise for a version that can handle failures.

@example

// Title: Delayed Message
import { Effect } from "effect"
const delay = (message: string) =>
Effect.promise<string>(
() =>
new Promise((resolve) => {
setTimeout(() => {
resolve(message)
}, 2000)
})
)
// ┌─── Effect<string, never, never>
// ▼
const program = delay("Async operation completed successfully!")

@since2.0.0

promise
(
() =>
new
var Promise: PromiseConstructor
new <string>(executor: (resolve: (value: string | PromiseLike<string>) => void, reject: (reason?: any) => void) => void) => Promise<string>

Creates a new Promise.

@paramexecutor A callback used to initialize the promise. This callback is passed two arguments: a resolve callback used to resolve the promise with a value or the result of another promise, and a reject callback used to reject the promise with a provided reason or error.

Promise
((
resolve: (value: string | PromiseLike<string>) => void
resolve
) => {
const
const rl: NodeReadLine.Interface
rl
=
module "node:readline"
NodeReadLine
.
function createInterface(options: NodeReadLine.ReadLineOptions): NodeReadLine.Interface (+1 overload)

The readline.createInterface() method creates a new readline.Interface instance.

import readline from 'node:readline';
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});

Once the readline.Interface instance is created, the most common case is to listen for the 'line' event:

rl.on('line', (line) => {
console.log(`Received: ${line}`);
});

If terminal is true for this instance then the output stream will get the best compatibility if it defines an output.columns property and emits a 'resize' event on the output if or when the columns ever change (process.stdout does this automatically when it is a TTY).

When creating a readline.Interface using stdin as input, the program will not terminate until it receives an EOF character. To exit without waiting for user input, call process.stdin.unref().

@sincev0.1.98

createInterface
({
ReadLineOptions.input: NodeJS.ReadableStream

The Readable stream to listen to

input
:
var process: NodeJS.Process
process
.
NodeJS.Process.stdin: NodeJS.ReadStream & {
fd: 0;
}

The process.stdin property returns a stream connected tostdin (fd 0). It is a net.Socket (which is a Duplex stream) unless fd 0 refers to a file, in which case it is a Readable stream.

For details of how to read from stdin see readable.read().

As a Duplex stream, process.stdin can also be used in "old" mode that is compatible with scripts written for Node.js prior to v0.10. For more information see Stream compatibility.

In "old" streams mode the stdin stream is paused by default, so one must call process.stdin.resume() to read from it. Note also that calling process.stdin.resume() itself would switch stream to "old" mode.

stdin
,
ReadLineOptions.output?: NodeJS.WritableStream | undefined

The Writable stream to write readline data to.

output
:
var process: NodeJS.Process
process
.
NodeJS.Process.stdout: NodeJS.WriteStream & {
fd: 1;
}

The process.stdout property returns a stream connected tostdout (fd 1). It is a net.Socket (which is a Duplex stream) unless fd 1 refers to a file, in which case it is a Writable stream.

For example, to copy process.stdin to process.stdout:

import { stdin, stdout } from 'node:process';
stdin.pipe(stdout);

process.stdout differs from other Node.js streams in important ways. See note on process I/O for more information.

stdout
})
const rl: NodeReadLine.Interface
rl
.
Interface.question(query: string, callback: (answer: string) => void): void (+1 overload)

The rl.question() method displays the query by writing it to the output, waits for user input to be provided on input, then invokes the callback function passing the provided input as the first argument.

When called, rl.question() will resume the input stream if it has been paused.

If the Interface was created with output set to null or undefined the query is not written.

The callback function passed to rl.question() does not follow the typical pattern of accepting an Error object or null as the first argument. The callback is called with the provided answer as the only argument.

An error will be thrown if calling rl.question() after rl.close().

Example usage:

rl.question('What is your favorite food? ', (answer) => {
console.log(`Oh, so your favorite food is ${answer}`);
});

Using an AbortController to cancel a question.

const ac = new AbortController();
const signal = ac.signal;
rl.question('What is your favorite food? ', { signal }, (answer) => {
console.log(`Oh, so your favorite food is ${answer}`);
});
signal.addEventListener('abort', () => {
console.log('The food question timed out');
}, { once: true });
setTimeout(() => ac.abort(), 10000);

@sincev0.3.3

@paramquery A statement or query to write to output, prepended to the prompt.

@paramcallback A callback function that is invoked with the user's input in response to the query.

question
(
message: string
message
, (
answer: string
answer
) => {
const rl: NodeReadLine.Interface
rl
.
Interface.close(): void

The rl.close() method closes the Interface instance and relinquishes control over the input and output streams. When called, the 'close' event will be emitted.

Calling rl.close() does not immediately stop other events (including 'line') from being emitted by the Interface instance.

@sincev0.1.98

close
()
resolve: (value: string | PromiseLike<string>) => void
resolve
(
answer: string
answer
)
})
})
)
const
const getNames: Effect.Effect<Chunk.Chunk<string>, never, never>
getNames
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<void, never, never>>, Chunk.Chunk<string>>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<void, never, never>>, Chunk.Chunk<...>, 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* () {
const
const ref: Ref.Ref<Chunk.Chunk<string>>
ref
= yield*
import Ref
Ref
.
const make: <Chunk.Chunk<string>>(value: Chunk.Chunk<string>) => Effect.Effect<Ref.Ref<Chunk.Chunk<string>>, never, never>

@since2.0.0

make
(
import Chunk
Chunk
.
const empty: <string>() => Chunk.Chunk<string>

@since2.0.0

empty
<string>())
// Fiber 1: Reading names from user input
const
const fiber1: Fiber.RuntimeFiber<void, never>
fiber1
= yield*
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const fork: <void, never, never>(self: Effect.Effect<void, never, never>) => Effect.Effect<Fiber.RuntimeFiber<void, never>, never, never>

Returns an effect that forks this effect into its own separate fiber, returning the fiber immediately, without waiting for it to begin executing the effect.

You can use the fork method whenever you want to execute an effect in a new fiber, concurrently and without "blocking" the fiber executing other effects. Using fibers can be tricky, so instead of using this method directly, consider other higher-level methods, such as raceWith, zipPar, and so forth.

The fiber returned by this method has methods to interrupt the fiber and to wait for it to finish executing the effect. See Fiber for more information.

Whenever you use this method to launch a new fiber, the new fiber is attached to the parent fiber's scope. This means when the parent fiber terminates, the child fiber will be terminated as well, ensuring that no fibers leak. This behavior is called "auto supervision", and if this behavior is not desired, you may use the forkDaemon or forkIn methods.

@since2.0.0

fork
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<void, never, never>>, void>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<void, never, never>>, void, never>) => Effect.Effect<...> (+1 overload)

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

When to Use

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* () {
while (true) {
const
const name: string
name
= yield*
const readLine: (message: string) => Effect.Effect<string>
readLine
(
"Please enter a name or `q` to exit: "
)
if (
const name: string
name
=== "q") {
break
}
yield*
import Ref
Ref
.
const update: <Chunk.Chunk<string>>(self: Ref.Ref<Chunk.Chunk<string>>, f: (a: Chunk.Chunk<string>) => Chunk.Chunk<string>) => Effect.Effect<void> (+1 overload)

@since2.0.0

update
(
const ref: Ref.Ref<Chunk.Chunk<string>>
ref
, (
state: Chunk.Chunk<string>
state
) =>
import Chunk
Chunk
.
const append: <string, string>(self: Chunk.Chunk<string>, a: string) => Chunk.NonEmptyChunk<string> (+1 overload)

Appends the specified element to the end of the Chunk.

@since2.0.0

append
(
state: Chunk.Chunk<string>
state
,
const name: string
name
))
}
})
)
// Fiber 2: Updating the state with predefined names
const
const fiber2: Fiber.RuntimeFiber<void, never>
fiber2
= yield*
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const fork: <void, never, never>(self: Effect.Effect<void, never, never>) => Effect.Effect<Fiber.RuntimeFiber<void, never>, never, never>

Returns an effect that forks this effect into its own separate fiber, returning the fiber immediately, without waiting for it to begin executing the effect.

You can use the fork method whenever you want to execute an effect in a new fiber, concurrently and without "blocking" the fiber executing other effects. Using fibers can be tricky, so instead of using this method directly, consider other higher-level methods, such as raceWith, zipPar, and so forth.

The fiber returned by this method has methods to interrupt the fiber and to wait for it to finish executing the effect. See Fiber for more information.

Whenever you use this method to launch a new fiber, the new fiber is attached to the parent fiber's scope. This means when the parent fiber terminates, the child fiber will be terminated as well, ensuring that no fibers leak. This behavior is called "auto supervision", and if this behavior is not desired, you may use the forkDaemon or forkIn methods.

@since2.0.0

fork
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<void, never, never>>, void>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<void, never, never>>, void, never>) => Effect.Effect<...> (+1 overload)

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

When to Use

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* () {
for (const
const name: string
name
of ["John", "Jane", "Joe", "Tom"]) {
yield*
import Ref
Ref
.
const update: <Chunk.Chunk<string>>(self: Ref.Ref<Chunk.Chunk<string>>, f: (a: Chunk.Chunk<string>) => Chunk.Chunk<string>) => Effect.Effect<void> (+1 overload)

@since2.0.0

update
(
const ref: Ref.Ref<Chunk.Chunk<string>>
ref
, (
state: Chunk.Chunk<string>
state
) =>
import Chunk
Chunk
.
const append: <string, string>(self: Chunk.Chunk<string>, a: string) => Chunk.NonEmptyChunk<string> (+1 overload)

Appends the specified element to the end of the Chunk.

@since2.0.0

append
(
state: Chunk.Chunk<string>
state
,
const name: string
name
))
yield*
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const sleep: (duration: DurationInput) => Effect.Effect<void>

Returns an effect that suspends for the specified duration. This method is asynchronous, and does not actually block the fiber executing the effect.

@since2.0.0

sleep
("1 second")
}
})
)
yield*
import Fiber
Fiber
.
const join: <void, never>(self: Fiber.Fiber<void, never>) => Effect.Effect<void, never, never>

Joins the fiber, which suspends the joining fiber until the result of the fiber has been determined. Attempting to join a fiber that has erred will result in a catchable error. Joining an interrupted fiber will result in an "inner interruption" of this fiber, unlike interruption triggered by another fiber, "inner interruption" can be caught and recovered.

@since2.0.0

join
(
const fiber1: Fiber.RuntimeFiber<void, never>
fiber1
)
yield*
import Fiber
Fiber
.
const join: <void, never>(self: Fiber.Fiber<void, never>) => Effect.Effect<void, never, never>

Joins the fiber, which suspends the joining fiber until the result of the fiber has been determined. Attempting to join a fiber that has erred will result in a catchable error. Joining an interrupted fiber will result in an "inner interruption" of this fiber, unlike interruption triggered by another fiber, "inner interruption" can be caught and recovered.

@since2.0.0

join
(
const fiber2: Fiber.RuntimeFiber<void, never>
fiber2
)
return yield*
import Ref
Ref
.
const get: <Chunk.Chunk<string>>(self: Ref.Ref<Chunk.Chunk<string>>) => Effect.Effect<Chunk.Chunk<string>, never, never>

@since2.0.0

get
(
const ref: Ref.Ref<Chunk.Chunk<string>>
ref
)
})
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const runPromise: <Chunk.Chunk<string>, never>(effect: Effect.Effect<Chunk.Chunk<string>, 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
(
const getNames: Effect.Effect<Chunk.Chunk<string>, never, never>
getNames
).
Promise<Chunk<string>>.then<void, never>(onfulfilled?: ((value: Chunk.Chunk<string>) => void | PromiseLike<void>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | 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
.
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
)
/*
Output:
Please enter a name or `q` to exit: Alice
Please enter a name or `q` to exit: Bob
Please enter a name or `q` to exit: q
{
_id: "Chunk",
values: [ ... ]
}
*/