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.

In real-world applications, SynchronizedRef is useful when you need to execute effects, such as querying a database, and then update shared state based on the result. It ensures that updates happen sequentially, preserving consistency in concurrent environments.

Example (Concurrent Updates with SynchronizedRef)

In this example, we simulate fetching user ages concurrently and updating a shared state that stores the ages:

1
import {
import Effect
Effect
,
import SynchronizedRef
SynchronizedRef
} from "effect"
2
3
// Simulated API to get user age
4
const
const getUserAge: (userId: number) => Effect.Effect<number, never, never>
getUserAge
= (
(parameter) userId: number
userId
: number) =>
5
import Effect
Effect
.
const succeed: <number>(value: number) => Effect.Effect<number, 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
(
(parameter) userId: number
userId
* 10).
(method) Pipeable.pipe<Effect.Effect<number, never, never>, Effect.Effect<number, never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<number, never, never>) => Effect.Effect<number, 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
(10 -
(parameter) userId: number
userId
))
6
7
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* () {
8
// Initialize a SynchronizedRef to hold an array of ages
9
const
const ref: SynchronizedRef.SynchronizedRef<number[]>
ref
= yield*
import SynchronizedRef
SynchronizedRef
.
const make: <number[]>(value: number[]) => Effect.Effect<SynchronizedRef.SynchronizedRef<number[]>, never, never>
make
<number[]>([])
10
11
// Helper function to log state before each effect
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
,
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<number, never, never>>, number[]>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<number, never, never>>, number[], never>) => Effect.Effect<...> (+1 overload)
gen
(function* () {
24
const
const age: number
age
= yield*
const getUserAge: (userId: number) => Effect.Effect<number, never, never>
getUserAge
(
(parameter) id: number
id
)
25
return
(parameter) sumOfAges: number[]
sumOfAges
.
(method) Array<number>.concat(...items: (number | ConcatArray<number>)[]): number[] (+1 overload)

Combines two or more arrays. This method returns a new array without modifying any existing arrays.

concat
(
const age: number
age
)
26
})
27
)
28
)
29
30
// Run tasks concurrently with a limit of 2 concurrent tasks
31
yield*
import Effect
Effect
.
const all: <readonly [Effect.Effect<void, never, never>, Effect.Effect<void, never, never>, Effect.Effect<void, never, never>, Effect.Effect<void, never, never>], { ...; }>(arg: readonly [...], options?: { ...; } | undefined) => Effect.Effect<...>

Runs all the provided effects in sequence respecting the structure provided in input. Supports multiple arguments, a single argument tuple / array or record / struct.

all
([
const task: (id: number) => Effect.Effect<void, never, never>
task
(1),
const task: (id: number) => Effect.Effect<void, never, never>
task
(2),
const task: (id: number) => Effect.Effect<void, never, never>
task
(3),
const task: (id: number) => Effect.Effect<void, never, never>
task
(4)], {
32
(property) concurrency: number
concurrency
: 2
33
})
34
35
// Retrieve the updated value
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
38
})
39
40
import Effect
Effect
.
const runPromise: <number[], never>(effect: Effect.Effect<number[], never, never>, options?: { readonly signal?: AbortSignal; } | undefined) => Promise<number[]>

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 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
timestamp=... level=INFO fiber=#2 message="task 1" message=[]
44
timestamp=... level=INFO fiber=#3 message="task 2" message=[]
45
timestamp=... level=INFO fiber=#2 message="task 3" message="[
46
10
47
]"
48
timestamp=... level=INFO fiber=#3 message="task 4" message="[
49
10,
50
20
51
]"
52
[ 10, 20, 30, 40 ]
53
*/