Skip to content

SynchronizedRef

SynchronizedRef<A> serves as a mutable reference to a value of type A. With it, we can store immutable data and perform updates atomically and effectfully.

The distinctive function in SynchronizedRef is updateEffect. This function takes an effectful operation and executes it to modify the shared state. This is the key feature setting SynchronizedRef apart from Ref.

1
import {
import Effect
Effect
,
import SynchronizedRef
SynchronizedRef
} from "effect"
2
3
const
const program: Effect.Effect<string, never, never>
program
=
import Effect
Effect
.
const gen: <YieldWrap<Effect.Effect<void, never, never>>, string>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<void, never, never>>, string, never>) => Effect.Effect<...> (+1 overload)
gen
(function* () {
4
const
const ref: SynchronizedRef.SynchronizedRef<string>
ref
= yield*
import SynchronizedRef
SynchronizedRef
.
const make: <string>(value: string) => Effect.Effect<SynchronizedRef.SynchronizedRef<string>, never, never>
make
("current")
5
// Simulating an effectful update operation
6
const
const updateEffect: Effect.Effect<string, never, never>
updateEffect
=
import Effect
Effect
.
const succeed: <string>(value: string) => Effect.Effect<string, never, never>
succeed
("update")
7
yield*
import SynchronizedRef
SynchronizedRef
.
const updateEffect: <string, never, never>(self: SynchronizedRef.SynchronizedRef<string>, f: (a: string) => Effect.Effect<string, never, never>) => Effect.Effect<void, never, never> (+1 overload)
updateEffect
(
const ref: SynchronizedRef.SynchronizedRef<string>
ref
, () =>
const updateEffect: Effect.Effect<string, never, never>
updateEffect
)
8
const
const value: string
value
= yield*
import SynchronizedRef
SynchronizedRef
.
const get: <string>(self: SynchronizedRef.SynchronizedRef<string>) => Effect.Effect<string, never, never>
get
(
const ref: SynchronizedRef.SynchronizedRef<string>
ref
)
9
return
const value: string
value
10
})
11
12
import Effect
Effect
.
const runPromise: <string, never>(effect: Effect.Effect<string, never, never>, options?: { readonly signal?: AbortSignal; } | undefined) => Promise<string>

Runs an `Effect` workflow, returning a `Promise` which resolves with the result of the workflow or rejects with an 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
)
13
/*
14
Output:
15
update
16
*/

In real-world applications, there are scenarios where we need to execute an effect (e.g., querying a database) and then update the shared state accordingly. This is where SynchronizedRef shines, allowing us to update shared state in an actor-model fashion. We have a shared mutable state, but for each distinct command or message, we want to execute our effect and update the state.

We can pass an effectful program into every single update. All of these updates will be performed in parallel, but the results will be sequenced in such a way that they only affect the state at different times, resulting in a consistent state at the end.

In the following example, we send a getAge request for each user, updating the state accordingly:

1
import {
import Effect
Effect
,
import SynchronizedRef
SynchronizedRef
} from "effect"
2
3
// Simulate API
4
const
const getAge: (userId: number) => Effect.Effect<{ userId: number; age: number; }, never, never>
getAge
= (
(parameter) userId: number
userId
: number) =>
5
import Effect
Effect
.
const succeed: <{ userId: number; age: number; }>(value: { userId: number; age: number; }) => Effect.Effect<{ userId: number; age: number; }, never, never>
succeed
({
(property) userId: number
userId
,
(property) age: number
age
:
(parameter) userId: number
userId
* 10 })
6
7
const
const users: number[]
users
= [1, 2, 3, 4]
8
9
const
const meanAge: Effect.Effect<number, never, never>
meanAge
=
import Effect
Effect
.
const gen: <YieldWrap<Effect.Effect<SynchronizedRef.SynchronizedRef<number>, never, never>> | YieldWrap<Effect.Effect<number, never, never>> | YieldWrap<...>, number>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+1 overload)
gen
(function* () {
10
const
const ref: SynchronizedRef.SynchronizedRef<number>
ref
= yield*
import SynchronizedRef
SynchronizedRef
.
const make: <number>(value: number) => Effect.Effect<SynchronizedRef.SynchronizedRef<number>, never, never>
make
(0)
11
12
const
const log: <R, E, A>(label: string, effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>
log
= <
(type parameter) R in <R, E, A>(label: string, effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
R
,
(type parameter) E in <R, E, A>(label: string, effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
E
,
(type parameter) A in <R, E, A>(label: string, effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
A
>(
(parameter) label: string
label
: string,
(parameter) effect: Effect.Effect<A, E, R>
effect
:
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>(label: string, effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
A
,
(type parameter) E in <R, E, A>(label: string, effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
E
,
(type parameter) R in <R, E, A>(label: string, effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
R
>) =>
13
import Effect
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)
gen
(function* () {
14
const
const value: number
value
= yield*
import SynchronizedRef
SynchronizedRef
.
const get: <number>(self: SynchronizedRef.SynchronizedRef<number>) => Effect.Effect<number, never, never>
get
(
const ref: SynchronizedRef.SynchronizedRef<number>
ref
)
15
yield*
import Effect
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.

log
(`${
(parameter) label: string
label
} get: ${
const value: number
value
}`)
16
return yield*
(parameter) effect: Effect.Effect<A, E, R>
effect
17
})
18
19
const
const task: (id: number) => Effect.Effect<void, never, never>
task
= (
(parameter) id: number
id
: number) =>
20
const log: <never, never, void>(label: string, effect: Effect.Effect<void, never, never>) => Effect.Effect<void, never, never>
log
(
21
`task ${
(parameter) id: number
id
}`,
22
import SynchronizedRef
SynchronizedRef
.
const updateEffect: <number, never, never>(self: SynchronizedRef.SynchronizedRef<number>, f: (a: number) => Effect.Effect<number, never, never>) => Effect.Effect<void, never, never> (+1 overload)
updateEffect
(
const ref: SynchronizedRef.SynchronizedRef<number>
ref
, (
(parameter) sumOfAges: number
sumOfAges
) =>
23
import Effect
Effect
.
const gen: <YieldWrap<Effect.Effect<{ userId: number; age: number; }, never, never>>, number>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<{ userId: number; age: number; }, never, never>>, number, never>) => Effect.Effect<...> (+1 overload)
gen
(function* () {
24
const
const user: { userId: number; age: number; }
user
= yield*
const getAge: (userId: number) => Effect.Effect<{ userId: number; age: number; }, never, never>
getAge
(
(parameter) id: number
id
)
25
return
(parameter) sumOfAges: number
sumOfAges
+
const user: { userId: number; age: number; }
user
.
(property) age: number
age
26
})
27
)
28
)
29
30
yield*
const task: (id: number) => Effect.Effect<void, never, never>
task
(1).
(method) 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
(
31
import Effect
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)

The `Effect.zip` function allows you to combine two effects into a single effect. This combined effect yields a tuple containing the results of both input effects once they succeed. Note that `Effect.zip` processes effects sequentially: it first completes the effect on the left and then the effect on the right. If you want to run the effects concurrently, you can use the `concurrent` option.

zip
(
const task: (id: number) => Effect.Effect<void, never, never>
task
(2), {
(property) concurrent?: boolean | undefined
concurrent
: true }),
32
import Effect
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)

The `Effect.zip` function allows you to combine two effects into a single effect. This combined effect yields a tuple containing the results of both input effects once they succeed. Note that `Effect.zip` processes effects sequentially: it first completes the effect on the left and then the effect on the right. If you want to run the effects concurrently, you can use the `concurrent` option.

zip
(
const task: (id: number) => Effect.Effect<void, never, never>
task
(3), {
(property) concurrent?: boolean | undefined
concurrent
: true }),
33
import Effect
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)

The `Effect.zip` function allows you to combine two effects into a single effect. This combined effect yields a tuple containing the results of both input effects once they succeed. Note that `Effect.zip` processes effects sequentially: it first completes the effect on the left and then the effect on the right. If you want to run the effects concurrently, you can use the `concurrent` option.

zip
(
const task: (id: number) => Effect.Effect<void, never, never>
task
(4), {
(property) concurrent?: boolean | undefined
concurrent
: true })
34
)
35
36
const
const value: number
value
= yield*
import SynchronizedRef
SynchronizedRef
.
const get: <number>(self: SynchronizedRef.SynchronizedRef<number>) => Effect.Effect<number, never, never>
get
(
const ref: SynchronizedRef.SynchronizedRef<number>
ref
)
37
return
const value: number
value
/
const users: number[]
users
.
(property) Array<number>.length: number

Gets or sets the length of the array. This is a number one higher than the highest index in the array.

length
38
})
39
40
import Effect
Effect
.
const runPromise: <number, never>(effect: Effect.Effect<number, never, never>, options?: { readonly signal?: AbortSignal; } | undefined) => Promise<number>

Runs an `Effect` workflow, returning a `Promise` which resolves with the result of the workflow or rejects with an error.

runPromise
(
const meanAge: Effect.Effect<number, never, never>
meanAge
).
(method) Promise<number>.then<void, never>(onfulfilled?: ((value: number) => 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
)
41
/*
42
Output:
43
... fiber=#1 message="task 4 get: 0"
44
... fiber=#2 message="task 3 get: 40"
45
... fiber=#3 message="task 1 get: 70"
46
... fiber=#4 message="task 2 get: 80"
47
25
48
*/