Simplifying Excessive Nesting
Suppose you want to create a custom function elapsed
that prints the elapsed time taken by an effect to execute.
Initially, you may come up with code that uses the standard pipe
method, but this approach can lead to excessive nesting and result in verbose and hard-to-read code:
1import { import Effect
Effect, import Console
Console } from "effect"2
3// Get the current timestamp4const const now: Effect.Effect<number, never, never>
now = import Effect
Effect.const sync: <number>(thunk: LazyArg<number>) => Effect.Effect<number, never, never>
Creates an `Effect` that represents a synchronous side-effectful computation.
The provided function (`thunk`) should not throw errors; if it does, the error is treated as a defect.
Use `Effect.sync` when you are certain the operation will not fail.
sync(() => new var Date: DateConstructor
new () => Date (+3 overloads)
Date().(method) Date.getTime(): number
Returns the stored time value in milliseconds since midnight, January 1, 1970 UTC.
getTime())5
6// Prints the elapsed time occurred to `self` to execute7const const elapsed: <R, E, A>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>
elapsed = <(type parameter) R in <R, E, A>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
R, (type parameter) E in <R, E, A>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
E, (type parameter) A in <R, E, A>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
A>(8 (parameter) self: Effect.Effect<A, E, R>
self: import Effect
Effect.interface Effect<out A, out E = never, out R = never>
namespace Effect
The `Effect` interface defines a value that lazily describes a workflow or job.
The workflow requires some context `R`, and may fail with an error of type `E`,
or succeed with a value of type `A`.
`Effect` values model resourceful interaction with the outside world, including
synchronous, asynchronous, concurrent, and parallel interaction. They use a
fiber-based concurrency model, with built-in support for scheduling, fine-grained
interruption, structured concurrency, and high scalability.
To run an `Effect` value, you need a `Runtime`, which is a type that is capable
of executing `Effect` values.
Effect<(type parameter) A in <R, E, A>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
A, (type parameter) E in <R, E, A>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
E, (type parameter) R in <R, E, A>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
R>9): import Effect
Effect.interface Effect<out A, out E = never, out R = never>
namespace Effect
The `Effect` interface defines a value that lazily describes a workflow or job.
The workflow requires some context `R`, and may fail with an error of type `E`,
or succeed with a value of type `A`.
`Effect` values model resourceful interaction with the outside world, including
synchronous, asynchronous, concurrent, and parallel interaction. They use a
fiber-based concurrency model, with built-in support for scheduling, fine-grained
interruption, structured concurrency, and high scalability.
To run an `Effect` value, you need a `Runtime`, which is a type that is capable
of executing `Effect` values.
Effect<(type parameter) A in <R, E, A>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
A, (type parameter) E in <R, E, A>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
E, (type parameter) R in <R, E, A>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
R> =>10 const now: Effect.Effect<number, never, never>
now.(method) Pipeable.pipe<Effect.Effect<number, never, never>, Effect.Effect<NoInfer<A>, E, R>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<number, never, never>) => Effect.Effect<NoInfer<A>, E, R>): Effect.Effect<...> (+21 overloads)
pipe(11 import Effect
Effect.const andThen: <number, Effect.Effect<NoInfer<A>, E, R>>(f: (a: number) => Effect.Effect<NoInfer<A>, E, R>) => <E, R>(self: Effect.Effect<number, E, R>) => Effect.Effect<...> (+3 overloads)
Executes a sequence of two actions, typically two `Effect`s, where the second action can depend on the result of the first action.
The `that` action can take various forms:
- a value
- a function returning a value
- a promise
- a function returning a promise
- an effect
- a function returning an effect
andThen(((parameter) startMillis: number
startMillis) =>12 (parameter) self: Effect.Effect<A, E, R>
self.(method) Pipeable.pipe<Effect.Effect<A, E, R>, Effect.Effect<NoInfer<A>, E, R>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<A, E, R>) => Effect.Effect<NoInfer<A>, E, R>): Effect.Effect<...> (+21 overloads)
pipe(13 import Effect
Effect.const andThen: <A, Effect.Effect<NoInfer<A>, never, never>>(f: (a: NoInfer<A>) => Effect.Effect<NoInfer<A>, never, never>) => <E, R>(self: Effect.Effect<...>) => Effect.Effect<...> (+3 overloads)
Executes a sequence of two actions, typically two `Effect`s, where the second action can depend on the result of the first action.
The `that` action can take various forms:
- a value
- a function returning a value
- a promise
- a function returning a promise
- an effect
- a function returning an effect
andThen(((parameter) result: NoInfer<A>
result) =>14 const now: Effect.Effect<number, never, never>
now.(method) Pipeable.pipe<Effect.Effect<number, never, never>, Effect.Effect<NoInfer<A>, never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<number, never, never>) => Effect.Effect<NoInfer<A>, never, never>): Effect.Effect<...> (+21 overloads)
pipe(15 import Effect
Effect.const andThen: <number, Effect.Effect<NoInfer<A>, never, never>>(f: (a: number) => Effect.Effect<NoInfer<A>, never, never>) => <E, R>(self: Effect.Effect<number, E, R>) => Effect.Effect<...> (+3 overloads)
Executes a sequence of two actions, typically two `Effect`s, where the second action can depend on the result of the first action.
The `that` action can take various forms:
- a value
- a function returning a value
- a promise
- a function returning a promise
- an effect
- a function returning an effect
andThen(((parameter) endMillis: number
endMillis) => {16 // Calculate the elapsed time in milliseconds17 const const elapsed: number
elapsed = (parameter) endMillis: number
endMillis - (parameter) startMillis: number
startMillis18 // Log the elapsed time19 return import Console
Console.const log: (...args: ReadonlyArray<any>) => Effect.Effect<void>
log(`Elapsed: ${const elapsed: number
elapsed}`).(method) Pipeable.pipe<Effect.Effect<void, never, never>, Effect.Effect<NoInfer<A>, never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<void, never, never>) => Effect.Effect<NoInfer<A>, never, never>): Effect.Effect<...> (+21 overloads)
pipe(20 import Effect
Effect.const map: <void, NoInfer<A>>(f: (a: void) => NoInfer<A>) => <E, R>(self: Effect.Effect<void, E, R>) => Effect.Effect<NoInfer<A>, E, R> (+1 overload)
map(() => (parameter) result: NoInfer<A>
result)21 )22 })23 )24 )25 )26 )27 )28
29// Simulates a successful computation with a delay of 200 milliseconds30const const task: Effect.Effect<string, never, never>
task = import Effect
Effect.const succeed: <string>(value: string) => Effect.Effect<string, never, never>
Creates an `Effect` that succeeds with the provided value.
Use this function to represent a successful computation that yields a value of type `A`.
The effect does not fail and does not require any environmental context.
succeed("some task").(method) Pipeable.pipe<Effect.Effect<string, never, never>, Effect.Effect<string, never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<string, never, never>) => Effect.Effect<string, never, never>): Effect.Effect<...> (+21 overloads)
pipe(import Effect
Effect.const delay: (duration: DurationInput) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R> (+1 overload)
Returns an effect that is delayed from this effect by the specified
`Duration`.
delay("200 millis"))31
32const const program: Effect.Effect<string, never, never>
program = const elapsed: <never, never, string>(self: Effect.Effect<string, never, never>) => Effect.Effect<string, never, never>
elapsed(const task: Effect.Effect<string, never, never>
task)33
34import Effect
Effect.const runPromise: <string, never>(effect: Effect.Effect<string, never, never>, options?: {
readonly signal?: AbortSignal;
} | undefined) => Promise<string>
Executes an effect and returns a `Promise` that resolves with the result.
Use `runPromise` when working with asynchronous effects and you need to integrate with code that uses Promises.
If the effect fails, the returned Promise will be rejected with the error.
runPromise(const program: Effect.Effect<string, never, never>
program).(method) Promise<string>.then<void, never>(onfulfilled?: ((value: 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.
then(namespace console
var console: Console
The `console` module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
* A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream.
* A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and
[`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module.
_**Warning**_: The global console object's methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for
more information.
Example using the global `console`:
```js
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
```
Example using the `Console` class:
```js
const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err
```
console.(method) globalThis.Console.log(message?: any, ...optionalParams: any[]): void
Prints to `stdout` with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html)
(the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)).
```js
const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout
```
See [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.
log)35/*36Output:37Elapsed: 20438some task39*/
To address this issue and make the code more manageable, there is a solution: the “do simulation.”
The “do simulation” in Effect allows you to write code in a more declarative style, similar to the “do notation” in other programming languages. It provides a way to define variables and perform operations on them using functions like Effect.bind
and Effect.let
.
Here’s how the do simulation works:
-
Start the do simulation using the
Effect.Do
value:const program = Effect.Do.pipe(/* ... rest of the code */) -
Within the do simulation scope, you can use the
Effect.bind
function to define variables and bind them toEffect
values:Effect.bind("variableName", (scope) => effectValue)variableName
is the name you choose for the variable you want to define. It must be unique within the scope.effectValue
is theEffect
value that you want to bind to the variable. It can be the result of a function call or any other validEffect
value.
-
You can accumulate multiple
Effect.bind
statements to define multiple variables within the scope:Effect.bind("variable1", () => effectValue1),Effect.bind("variable2", ({ variable1 }) => effectValue2),// ... additional bind statements -
Inside the do simulation scope, you can also use the
Effect.let
function to define variables and bind them to simple values:Effect.let("variableName", (scope) => simpleValue)variableName
is the name you give to the variable. Like before, it must be unique within the scope.simpleValue
is the value you want to assign to the variable. It can be a simple value like anumber
,string
, orboolean
.
-
Regular Effect functions like
Effect.andThen
,Effect.flatMap
,Effect.tap
, andEffect.map
can still be used within the do simulation. These functions will receive the accumulated variables as arguments within the scope:Effect.andThen(({ variable1, variable2 }) => {// Perform operations using variable1 and variable2// Return an `Effect` value as the result})
With the do simulation, you can rewrite the elapsed
combinator like this:
1import { import Effect
Effect, import Console
Console } from "effect"2
3// Get the current timestamp4const const now: Effect.Effect<number, never, never>
now = import Effect
Effect.const sync: <number>(thunk: LazyArg<number>) => Effect.Effect<number, never, never>
Creates an `Effect` that represents a synchronous side-effectful computation.
The provided function (`thunk`) should not throw errors; if it does, the error is treated as a defect.
Use `Effect.sync` when you are certain the operation will not fail.
sync(() => new var Date: DateConstructor
new () => Date (+3 overloads)
Date().(method) Date.getTime(): number
Returns the stored time value in milliseconds since midnight, January 1, 1970 UTC.
getTime())5
6const const elapsed: <R, E, A>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>
elapsed = <(type parameter) R in <R, E, A>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
R, (type parameter) E in <R, E, A>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
E, (type parameter) A in <R, E, A>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
A>(7 (parameter) self: Effect.Effect<A, E, R>
self: import Effect
Effect.interface Effect<out A, out E = never, out R = never>
namespace Effect
The `Effect` interface defines a value that lazily describes a workflow or job.
The workflow requires some context `R`, and may fail with an error of type `E`,
or succeed with a value of type `A`.
`Effect` values model resourceful interaction with the outside world, including
synchronous, asynchronous, concurrent, and parallel interaction. They use a
fiber-based concurrency model, with built-in support for scheduling, fine-grained
interruption, structured concurrency, and high scalability.
To run an `Effect` value, you need a `Runtime`, which is a type that is capable
of executing `Effect` values.
Effect<(type parameter) A in <R, E, A>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
A, (type parameter) E in <R, E, A>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
E, (type parameter) R in <R, E, A>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
R>8): import Effect
Effect.interface Effect<out A, out E = never, out R = never>
namespace Effect
The `Effect` interface defines a value that lazily describes a workflow or job.
The workflow requires some context `R`, and may fail with an error of type `E`,
or succeed with a value of type `A`.
`Effect` values model resourceful interaction with the outside world, including
synchronous, asynchronous, concurrent, and parallel interaction. They use a
fiber-based concurrency model, with built-in support for scheduling, fine-grained
interruption, structured concurrency, and high scalability.
To run an `Effect` value, you need a `Runtime`, which is a type that is capable
of executing `Effect` values.
Effect<(type parameter) A in <R, E, A>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
A, (type parameter) E in <R, E, A>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
E, (type parameter) R in <R, E, A>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
R> =>9 import Effect
Effect.const Do: Effect.Effect<{}, never, never>
The "do simulation" in Effect allows you to write code in a more declarative style, similar to the "do notation" in other programming languages. It provides a way to define variables and perform operations on them using functions like `bind` and `let`.
Here's how the do simulation works:
1. Start the do simulation using the `Do` value
2. Within the do simulation scope, you can use the `bind` function to define variables and bind them to `Effect` values
3. You can accumulate multiple `bind` statements to define multiple variables within the scope
4. Inside the do simulation scope, you can also use the `let` function to define variables and bind them to simple values
Do.(method) Pipeable.pipe<Effect.Effect<{}, never, never>, Effect.Effect<{
startMillis: number;
}, never, never>, Effect.Effect<{
startMillis: number;
result: A;
}, E, R>, Effect.Effect<...>, Effect.Effect<...>, Effect.Effect<...>, Effect.Effect<...>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<...>) => Effect.Effect<...>, bc: (_: Effect.Effect<...>) => Effect.Effect<...>, cd: (_: Effect.Effect<...>) => Effect.Effect<...>, de: (_: Effect.Effect<...>) => Effect.Effect<...>, ef: (_: Effect.Effect<...>) => Effect.Effect<...>, fg: (_: Effect.Effect<...>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe(10 import Effect
Effect.const bind: <"startMillis", {}, number, never, never>(name: "startMillis", f: (a: {}) => Effect.Effect<number, never, never>) => <E1, R1>(self: Effect.Effect<{}, E1, R1>) => Effect.Effect<...> (+1 overload)
The "do simulation" in Effect allows you to write code in a more declarative style, similar to the "do notation" in other programming languages. It provides a way to define variables and perform operations on them using functions like `bind` and `let`.
Here's how the do simulation works:
1. Start the do simulation using the `Do` value
2. Within the do simulation scope, you can use the `bind` function to define variables and bind them to `Effect` values
3. You can accumulate multiple `bind` statements to define multiple variables within the scope
4. Inside the do simulation scope, you can also use the `let` function to define variables and bind them to simple values
bind("startMillis", () => const now: Effect.Effect<number, never, never>
now),11 import Effect
Effect.const bind: <"result", {
startMillis: number;
}, A, E, R>(name: "result", f: (a: {
startMillis: number;
}) => Effect.Effect<A, E, R>) => <E1, R1>(self: Effect.Effect<{
startMillis: number;
}, E1, R1>) => Effect.Effect<...> (+1 overload)
The "do simulation" in Effect allows you to write code in a more declarative style, similar to the "do notation" in other programming languages. It provides a way to define variables and perform operations on them using functions like `bind` and `let`.
Here's how the do simulation works:
1. Start the do simulation using the `Do` value
2. Within the do simulation scope, you can use the `bind` function to define variables and bind them to `Effect` values
3. You can accumulate multiple `bind` statements to define multiple variables within the scope
4. Inside the do simulation scope, you can also use the `let` function to define variables and bind them to simple values
bind("result", () => (parameter) self: Effect.Effect<A, E, R>
self),12 import Effect
Effect.const bind: <"endMillis", {
startMillis: number;
result: A;
}, number, never, never>(name: "endMillis", f: (a: {
startMillis: number;
result: A;
}) => Effect.Effect<number, never, never>) => <E1, R1>(self: Effect.Effect<...>) => Effect.Effect<...> (+1 overload)
The "do simulation" in Effect allows you to write code in a more declarative style, similar to the "do notation" in other programming languages. It provides a way to define variables and perform operations on them using functions like `bind` and `let`.
Here's how the do simulation works:
1. Start the do simulation using the `Do` value
2. Within the do simulation scope, you can use the `bind` function to define variables and bind them to `Effect` values
3. You can accumulate multiple `bind` statements to define multiple variables within the scope
4. Inside the do simulation scope, you can also use the `let` function to define variables and bind them to simple values
bind("endMillis", () => const now: Effect.Effect<number, never, never>
now),13 import Effect
Effect.(alias) let<"elapsed", {
startMillis: number;
result: A;
endMillis: number;
}, number>(name: "elapsed", f: (a: {
startMillis: number;
result: A;
endMillis: number;
}) => number): <E, R>(self: Effect.Effect<{
startMillis: number;
result: A;
endMillis: number;
}, E, R>) => Effect.Effect<...> (+1 overload)
export let
let(14 "elapsed",15 ({ (parameter) startMillis: number
startMillis, (parameter) endMillis: number
endMillis }) => (parameter) endMillis: number
endMillis - (parameter) startMillis: number
startMillis // Calculate the elapsed time in milliseconds16 ),17 import Effect
Effect.const tap: <{
startMillis: number;
result: A;
endMillis: number;
elapsed: number;
}, Effect.Effect<void, never, never>>(f: (a: {
startMillis: number;
result: A;
endMillis: number;
elapsed: number;
}) => Effect.Effect<...>) => <E, R>(self: Effect.Effect<...>) => Effect.Effect<...> (+7 overloads)
tap(({ (parameter) elapsed: number
elapsed }) => import Console
Console.const log: (...args: ReadonlyArray<any>) => Effect.Effect<void>
log(`Elapsed: ${(parameter) elapsed: number
elapsed}`)), // Log the elapsed time18 import Effect
Effect.const map: <{
startMillis: number;
result: A;
endMillis: number;
elapsed: number;
}, A>(f: (a: {
startMillis: number;
result: A;
endMillis: number;
elapsed: number;
}) => A) => <E, R>(self: Effect.Effect<{
startMillis: number;
result: A;
endMillis: number;
elapsed: number;
}, E, R>) => Effect.Effect<...> (+1 overload)
map(({ (parameter) result: A
result }) => (parameter) result: A
result)19 )20
21// Simulates a successful computation with a delay of 200 milliseconds22const const task: Effect.Effect<string, never, never>
task = import Effect
Effect.const succeed: <string>(value: string) => Effect.Effect<string, never, never>
Creates an `Effect` that succeeds with the provided value.
Use this function to represent a successful computation that yields a value of type `A`.
The effect does not fail and does not require any environmental context.
succeed("some task").(method) Pipeable.pipe<Effect.Effect<string, never, never>, Effect.Effect<string, never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<string, never, never>) => Effect.Effect<string, never, never>): Effect.Effect<...> (+21 overloads)
pipe(import Effect
Effect.const delay: (duration: DurationInput) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R> (+1 overload)
Returns an effect that is delayed from this effect by the specified
`Duration`.
delay("200 millis"))23
24const const program: Effect.Effect<string, never, never>
program = const elapsed: <never, never, string>(self: Effect.Effect<string, never, never>) => Effect.Effect<string, never, never>
elapsed(const task: Effect.Effect<string, never, never>
task)25
26import Effect
Effect.const runPromise: <string, never>(effect: Effect.Effect<string, never, never>, options?: {
readonly signal?: AbortSignal;
} | undefined) => Promise<string>
Executes an effect and returns a `Promise` that resolves with the result.
Use `runPromise` when working with asynchronous effects and you need to integrate with code that uses Promises.
If the effect fails, the returned Promise will be rejected with the error.
runPromise(const program: Effect.Effect<string, never, never>
program).(method) Promise<string>.then<void, never>(onfulfilled?: ((value: 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.
then(namespace console
var console: Console
The `console` module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
* A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream.
* A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and
[`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module.
_**Warning**_: The global console object's methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for
more information.
Example using the global `console`:
```js
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
```
Example using the `Console` class:
```js
const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err
```
console.(method) globalThis.Console.log(message?: any, ...optionalParams: any[]): void
Prints to `stdout` with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html)
(the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)).
```js
const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout
```
See [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.
log)27/*28Output:29Elapsed: 20430some task31*/
In this solution, we use the do simulation to simplify the code. The elapsed
function now starts with Effect.Do
to enter the simulation scope.
Inside the scope, we use Effect.bind
to define variables and bind them to the corresponding effects.
The most concise and convenient solution is to use the Effect.gen
constructor, which allows you to work with generators when dealing with effects. This approach leverages the native scope provided by the generator syntax, avoiding excessive nesting and leading to more concise code.
1import { import Effect
Effect } from "effect"2
3// Get the current timestamp4const const now: Effect.Effect<number, never, never>
now = import Effect
Effect.const sync: <number>(thunk: LazyArg<number>) => Effect.Effect<number, never, never>
Creates an `Effect` that represents a synchronous side-effectful computation.
The provided function (`thunk`) should not throw errors; if it does, the error is treated as a defect.
Use `Effect.sync` when you are certain the operation will not fail.
sync(() => new var Date: DateConstructor
new () => Date (+3 overloads)
Date().(method) Date.getTime(): number
Returns the stored time value in milliseconds since midnight, January 1, 1970 UTC.
getTime())5
6// Prints the elapsed time occurred to `self` to execute7const const elapsed: <R, E, A>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>
elapsed = <(type parameter) R in <R, E, A>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
R, (type parameter) E in <R, E, A>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
E, (type parameter) A in <R, E, A>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
A>(8 (parameter) self: Effect.Effect<A, E, R>
self: import Effect
Effect.interface Effect<out A, out E = never, out R = never>
namespace Effect
The `Effect` interface defines a value that lazily describes a workflow or job.
The workflow requires some context `R`, and may fail with an error of type `E`,
or succeed with a value of type `A`.
`Effect` values model resourceful interaction with the outside world, including
synchronous, asynchronous, concurrent, and parallel interaction. They use a
fiber-based concurrency model, with built-in support for scheduling, fine-grained
interruption, structured concurrency, and high scalability.
To run an `Effect` value, you need a `Runtime`, which is a type that is capable
of executing `Effect` values.
Effect<(type parameter) A in <R, E, A>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
A, (type parameter) E in <R, E, A>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
E, (type parameter) R in <R, E, A>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
R>9): import Effect
Effect.interface Effect<out A, out E = never, out R = never>
namespace Effect
The `Effect` interface defines a value that lazily describes a workflow or job.
The workflow requires some context `R`, and may fail with an error of type `E`,
or succeed with a value of type `A`.
`Effect` values model resourceful interaction with the outside world, including
synchronous, asynchronous, concurrent, and parallel interaction. They use a
fiber-based concurrency model, with built-in support for scheduling, fine-grained
interruption, structured concurrency, and high scalability.
To run an `Effect` value, you need a `Runtime`, which is a type that is capable
of executing `Effect` values.
Effect<(type parameter) A in <R, E, A>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
A, (type parameter) E in <R, E, A>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
E, (type parameter) R in <R, E, A>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
R> =>10 import Effect
Effect.const gen: <YieldWrap<Effect.Effect<A, E, R>> | YieldWrap<Effect.Effect<number, never, never>>, A>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+1 overload)
gen(function* () {11 const const startMillis: number
startMillis = yield* const now: Effect.Effect<number, never, never>
now12 const const result: A
result = yield* (parameter) self: Effect.Effect<A, E, R>
self13 const const endMillis: number
endMillis = yield* const now: Effect.Effect<number, never, never>
now14 // Calculate the elapsed time in milliseconds15 const const elapsed: number
elapsed = const endMillis: number
endMillis - const startMillis: number
startMillis16 // Log the elapsed time17 namespace console
var console: Console
The `console` module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
* A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream.
* A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and
[`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module.
_**Warning**_: The global console object's methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for
more information.
Example using the global `console`:
```js
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
```
Example using the `Console` class:
```js
const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err
```
console.(method) Console.log(message?: any, ...optionalParams: any[]): void
Prints to `stdout` with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html)
(the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)).
```js
const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout
```
See [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.
log(`Elapsed: ${const elapsed: number
elapsed}`)18 return const result: A
result19 })20
21// Simulates a successful computation with a delay of 200 milliseconds22const const task: Effect.Effect<string, never, never>
task = import Effect
Effect.const succeed: <string>(value: string) => Effect.Effect<string, never, never>
Creates an `Effect` that succeeds with the provided value.
Use this function to represent a successful computation that yields a value of type `A`.
The effect does not fail and does not require any environmental context.
succeed("some task").(method) Pipeable.pipe<Effect.Effect<string, never, never>, Effect.Effect<string, never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<string, never, never>) => Effect.Effect<string, never, never>): Effect.Effect<...> (+21 overloads)
pipe(import Effect
Effect.const delay: (duration: DurationInput) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R> (+1 overload)
Returns an effect that is delayed from this effect by the specified
`Duration`.
delay("200 millis"))23
24const const program: Effect.Effect<string, never, never>
program = const elapsed: <never, never, string>(self: Effect.Effect<string, never, never>) => Effect.Effect<string, never, never>
elapsed(const task: Effect.Effect<string, never, never>
task)25
26import Effect
Effect.const runPromise: <string, never>(effect: Effect.Effect<string, never, never>, options?: {
readonly signal?: AbortSignal;
} | undefined) => Promise<string>
Executes an effect and returns a `Promise` that resolves with the result.
Use `runPromise` when working with asynchronous effects and you need to integrate with code that uses Promises.
If the effect fails, the returned Promise will be rejected with the error.
runPromise(const program: Effect.Effect<string, never, never>
program).(method) Promise<string>.then<void, never>(onfulfilled?: ((value: 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.
then(namespace console
var console: Console
The `console` module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
* A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream.
* A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and
[`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module.
_**Warning**_: The global console object's methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for
more information.
Example using the global `console`:
```js
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
```
Example using the `Console` class:
```js
const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err
```
console.(method) Console.log(message?: any, ...optionalParams: any[]): void
Prints to `stdout` with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html)
(the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)).
```js
const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout
```
See [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.
log)27/*28Output:29Elapsed: 20430some task31*/
In this solution, we switch to using generators to simplify the code. The elapsed
function now uses a generator function (Effect.gen
) to define the flow of execution. Within the generator, we use yield*
to invoke effects and bind their results to variables. This eliminates the nesting and provides a more readable and sequential code structure.
The generator style in Effect uses a more linear and sequential flow of execution, resembling traditional imperative programming languages. This makes the code easier to read and understand, especially for developers who are more familiar with imperative programming paradigms.
On the other hand, the pipe style can lead to excessive nesting, especially when dealing with complex effectful computations. This can make the code harder to follow and debug.
For more information on how to use generators in Effect, you can refer to the Using Generators in Effect guide.