1/**
2 * Stores fiber-safe mutable state inside Effect programs.
3 *
4 * A `Ref<A>` holds one value and exposes reads, writes, and atomic
5 * transformations as effects, so state changes compose with Effect's
6 * concurrency model. This module includes constructors, safe and unsafe reads,
7 * set and get-and-set helpers, update and modify helpers, and conditional
8 * update variants that leave the value unchanged when an `Option.none` result
9 * is returned.
10 *
11 * @since 2.0.0
12 */
13import * as Effect from "./Effect.ts"
14import { dual, identity } from "./Function.ts"
15import { PipeInspectableProto } from "./internal/core.ts"
16import * as MutableRef from "./MutableRef.ts"
17import type * as Option from "./Option.ts"
18import type { Pipeable } from "./Pipeable.ts"
19import type { Invariant } from "./Types.ts"
20
21const TypeId = "~effect/Ref"
22
23/**
24 * A mutable reference that provides atomic read, write, and update operations.
25 *
26 * **When to use**
27 *
28 * Use to keep shared mutable state that is read and updated inside Effect
29 * programs.
30 *
31 * **Details**
32 *
33 * A `Ref` is a thread-safe mutable reference type for shared state. It supports
34 * simple read and write operations as well as atomic transformations.
35 *
36 * **Example** (Reading and updating a ref)
37 *
38 * ```ts import.meta.vitest
39 * import { Effect, Ref } from "effect"
40 *
41 * const program = Effect.gen(function*() {
42 * const counter = yield* Ref.make(0)
43 * const value = yield* Ref.get(counter)
44 * yield* Ref.update(counter, (n) => n + 1)
45 * const newValue = yield* Ref.get(counter)
46 * return [value, newValue]
47 * })
48 *
49 * await Effect.runPromise(program) // => [0, 1]
50 * ```
51 *
52 * @see {@link make} for creating a `Ref`
53 * @see {@link get} for reading the current value
54 * @see {@link set} for replacing the current value
55 *
56 * @category models
57 * @since 2.0.0
58 */
59export interface Ref<in out A> extends Ref.Variance<A>, Pipeable {
60 readonly ref: MutableRef.MutableRef<A>
61}
62
63/**
64 * The Ref namespace containing type definitions and utilities.
65 *
66 * **When to use**
67 *
68 * Use when referring to type members nested under the `Ref` namespace.
69 *
70 * @since 2.0.0
71 */
72export declare namespace Ref {
73 /**
74 * Variance interface for Ref types, defining the type parameter constraints.
75 *
76 * **When to use**
77 *
78 * Use when working with the type-level variance marker carried by `Ref`.
79 *
80 * **Example** (Using invariant refs)
81 *
82 * ```ts import.meta.vitest
83 * import { Effect, Ref } from "effect"
84 *
85 * const program = Effect.gen(function*() {
86 * const ref = yield* Ref.make(42)
87 * const value = yield* Ref.get(ref)
88 * yield* Ref.set(ref, value + 1)
89 * return yield* Ref.get(ref)
90 * })
91 *
92 * await Effect.runPromise(program) // => 43
93 * ```
94 *
95 * @category models
96 * @since 2.0.0
97 */
98 export interface Variance<in out A> {
99 readonly [TypeId]: {
100 readonly _A: Invariant<A>
101 }
102 }
103}
104
105const RefProto = {
106 [TypeId]: {
107 _A: identity
108 },
109 ...PipeInspectableProto,
110 toJSON(this: Ref<any>) {
111 return {
112 _id: "Ref",
113 ref: this.ref
114 }
115 }
116}
117
118/**
119 * Creates a new Ref with the specified initial value (unsafe version).
120 *
121 * **When to use**
122 *
123 * Use when you need immediate synchronous construction and can guarantee
124 * that creating the `Ref` outside of `Effect` is safe.
125 *
126 * **Gotchas**
127 *
128 * Prefer `Ref.make` for Effect-wrapped creation in Effect programs.
129 *
130 * **Example** (Creating a ref unsafely)
131 *
132 * ```ts import.meta.vitest
133 * import { Ref } from "effect"
134 *
135 * const counter = Ref.makeUnsafe(0)
136 * Ref.getUnsafe(counter) // => 0
137 * ```
138 *
139 * @category constructors
140 * @since 4.0.0
141 */
142export const makeUnsafe = <A>(value: A): Ref<A> => {
143 const self = Object.create(RefProto)
144 self.ref = MutableRef.make(value)
145 return self
146}
147
148/**
149 * Creates a new Ref with the specified initial value.
150 *
151 * **When to use**
152 *
153 * Use to create a `Ref` for shared mutable state inside an Effect program.
154 *
155 * **Example** (Creating a ref)
156 *
157 * ```ts import.meta.vitest
158 * import { Effect, Ref } from "effect"
159 *
160 * const program = Effect.gen(function*() {
161 * const ref = yield* Ref.make(42)
162 * return yield* Ref.get(ref)
163 * })
164 *
165 * await Effect.runPromise(program) // => 42
166 * ```
167 *
168 * @see {@link makeUnsafe} for synchronous construction outside Effect code
169 *
170 * @category constructors
171 * @since 2.0.0
172 */
173export const make = <A>(value: A): Effect.Effect<Ref<A>> => Effect.sync(() => makeUnsafe(value))
174
175/**
176 * Gets the current value of the Ref.
177 *
178 * **When to use**
179 *
180 * Use to read the current `Ref` value without changing it.
181 *
182 * **Example** (Getting the current value)
183 *
184 * ```ts import.meta.vitest
185 * import { Effect, Ref } from "effect"
186 *
187 * const program = Effect.gen(function*() {
188 * const ref = yield* Ref.make(42)
189 * return yield* Ref.get(ref)
190 * })
191 *
192 * await Effect.runPromise(program) // => 42
193 * ```
194 *
195 * @see {@link set} for replacing the current value
196 *
197 * @category getters
198 * @since 2.0.0
199 */
200export const get = <A>(self: Ref<A>) => Effect.sync(() => self.ref.current)
201
202/**
203 * Sets the value of the Ref to the specified value.
204 *
205 * **When to use**
206 *
207 * Use to replace the current `Ref` value with a known value.
208 *
209 * **Example** (Setting a value)
210 *
211 * ```ts import.meta.vitest
212 * import { Effect, Ref } from "effect"
213 *
214 * const program = Effect.gen(function*() {
215 * const ref = yield* Ref.make(0)
216 * yield* Ref.set(ref, 42)
217 * return yield* Ref.get(ref)
218 * })
219 *
220 * const program2 = Effect.gen(function*() {
221 * const ref = yield* Ref.make(0)
222 * yield* Ref.set(ref, 100)
223 * return yield* Ref.get(ref)
224 * })
225 *
226 * await Effect.runPromise(program) // => 42
227 * await Effect.runPromise(program2) // => 100
228 * ```
229 *
230 * @see {@link getAndSet} for setting while returning the previous value
231 * @see {@link setAndGet} for setting while returning the new value
232 *
233 * @category mutations
234 * @since 2.0.0
235 */
236export const set = dual<
237 <A>(value: A) => (self: Ref<A>) => Effect.Effect<void>,
238 <A>(self: Ref<A>, value: A) => Effect.Effect<void>
239>(2, <A>(self: Ref<A>, value: A) => Effect.sync(() => MutableRef.set(self.ref, value)))
240
241/**
242 * Gets the current value of the Ref, sets it to the specified value, and returns the previous value atomically.
243 *
244 * **When to use**
245 *
246 * Use to replace a plain `Ref` value while returning the previous value.
247 *
248 * **Example** (Replacing a value atomically)
249 *
250 * ```ts import.meta.vitest
251 * import { Effect, Ref } from "effect"
252 *
253 * const program = Effect.gen(function*() {
254 * const ref = yield* Ref.make("initial")
255 *
256 * const previous = yield* Ref.getAndSet(ref, "updated")
257 * const current = yield* Ref.get(ref)
258 * return [previous, current]
259 * })
260 *
261 * await Effect.runPromise(program) // => ["initial", "updated"]
262 * ```
263 *
264 * @see {@link set} for setting without returning the previous value
265 * @see {@link getAndUpdate} for deriving the new value from the previous value
266 *
267 * @category mutations
268 * @since 2.0.0
269 */
270export const getAndSet = dual<
271 <A>(value: A) => (self: Ref<A>) => Effect.Effect<A>,
272 <A>(self: Ref<A>, value: A) => Effect.Effect<A>
273>(2, <A>(self: Ref<A>, value: A) =>
274 Effect.sync(() => {
275 const current = self.ref.current
276 self.ref.current = value
277 return current
278 }))
279
280/**
281 * Gets the current value of the Ref, updates it with the given function, and returns the previous value atomically.
282 *
283 * **When to use**
284 *
285 * Use to derive a new `Ref` value while returning the previous value.
286 *
287 * **Example** (Updating and returning the previous value)
288 *
289 * ```ts import.meta.vitest
290 * import { Effect, Ref } from "effect"
291 *
292 * const program = Effect.gen(function*() {
293 * const counter = yield* Ref.make(10)
294 *
295 * const previous = yield* Ref.getAndUpdate(counter, (n) => n * 2)
296 * const current = yield* Ref.get(counter)
297 * return [previous, current]
298 * })
299 *
300 * await Effect.runPromise(program) // => [10, 20]
301 * ```
302 *
303 * @see {@link update} for updating without returning the previous value
304 * @see {@link updateAndGet} for returning the new value instead
305 *
306 * @category mutations
307 * @since 2.0.0
308 */
309export const getAndUpdate = dual<
310 <A>(f: (a: A) => A) => (self: Ref<A>) => Effect.Effect<A>,
311 <A>(self: Ref<A>, f: (a: A) => A) => Effect.Effect<A>
312>(2, <A>(self: Ref<A>, f: (a: A) => A) =>
313 Effect.sync(() => {
314 const current = self.ref.current
315 self.ref.current = f(current)
316 return current
317 }))
318
319/**
320 * Gets the current value of the Ref and updates it atomically with the given partial function.
321 *
322 * **When to use**
323 *
324 * Use to return the previous `Ref` value while applying a conditional update.
325 *
326 * **Details**
327 *
328 * If the partial function returns `Option.some`, the Ref is updated with the
329 * new value. If it returns `Option.none`, the Ref is left unchanged. The effect
330 * always returns the value that was in the Ref before the attempted update.
331 *
332 * **Example** (Conditionally updating and returning the previous value)
333 *
334 * ```ts import.meta.vitest
335 * import { Effect, Option, Ref } from "effect"
336 *
337 * const program = Effect.gen(function*() {
338 * const counter = yield* Ref.make(5)
339 *
340 * const previous1 = yield* Ref.getAndUpdateSome(
341 * counter,
342 * (n) => n > 3 ? Option.some(n * 2) : Option.none()
343 * )
344 * const current1 = yield* Ref.get(counter)
345 * const previous2 = yield* Ref.getAndUpdateSome(
346 * counter,
347 * (n) => n < 3 ? Option.some(n * 2) : Option.none()
348 * )
349 * const current2 = yield* Ref.get(counter)
350 * return [previous1, current1, previous2, current2]
351 * })
352 *
353 * await Effect.runPromise(program) // => [5, 10, 10, 10]
354 * ```
355 *
356 * @see {@link getAndUpdate} for always applying an update
357 * @see {@link updateSome} for conditional updates without returning the previous value
358 *
359 * @category mutations
360 * @since 2.0.0
361 */
362export const getAndUpdateSome = dual<
363 <A>(pf: (a: A) => Option.Option<A>) => (self: Ref<A>) => Effect.Effect<A>,
364 <A>(self: Ref<A>, pf: (a: A) => Option.Option<A>) => Effect.Effect<A>
365>(2, <A>(self: Ref<A>, pf: (a: A) => Option.Option<A>) =>
366 Effect.sync(() => {
367 const current = self.ref.current
368 const option = pf(current)
369 if (option._tag === "Some") {
370 self.ref.current = option.value
371 }
372 return current
373 }))
374
375/**
376 * Sets the value of the Ref atomically to the specified value and returns the new value.
377 *
378 * **When to use**
379 *
380 * Use when you want to set a `Ref` value and immediately get it back in one
381 * atomic operation.
382 *
383 * **Example** (Setting and returning the new value)
384 *
385 * ```ts import.meta.vitest
386 * import { Effect, Ref } from "effect"
387 *
388 * const program = Effect.gen(function*() {
389 * const ref = yield* Ref.make(10)
390 *
391 * const newValue = yield* Ref.setAndGet(ref, 42)
392 * const current = yield* Ref.get(ref)
393 * return [newValue, current]
394 * })
395 *
396 * const program2 = Effect.gen(function*() {
397 * const counter = yield* Ref.make(0)
398 * return yield* Ref.setAndGet(counter, 20)
399 * })
400 *
401 * await Effect.runPromise(program) // => [42, 42]
402 * await Effect.runPromise(program2) // => 20
403 * ```
404 *
405 * @category mutations
406 * @since 2.0.0
407 */
408export const setAndGet = dual<
409 <A>(value: A) => (self: Ref<A>) => Effect.Effect<A>,
410 <A>(self: Ref<A>, value: A) => Effect.Effect<A>
411>(2, <A>(self: Ref<A>, value: A) => Effect.sync(() => self.ref.current = value))
412
413/**
414 * Modifies the value of the Ref atomically using the given function.
415 *
416 * **When to use**
417 *
418 * Use to compute both a separate return value and the next stored `Ref` value
419 * in one atomic update.
420 *
421 * **Details**
422 *
423 * The function receives the current value and returns a tuple of
424 * `[result, newValue]`. The Ref is updated with `newValue`, and `result` is
425 * returned by the effect.
426 *
427 * **Example** (Modifying a value atomically)
428 *
429 * ```ts import.meta.vitest
430 * import { Effect, Ref } from "effect"
431 *
432 * const program = Effect.gen(function*() {
433 * const counter = yield* Ref.make(10)
434 *
435 * const result = yield* Ref.modify(counter, (n) => [
436 * `Previous value was ${n}`,
437 * n * 2
438 * ])
439 * const current = yield* Ref.get(counter)
440 * return [result, current]
441 * })
442 *
443 * const program2 = Effect.gen(function*() {
444 * const state = yield* Ref.make({ count: 0, total: 0 })
445 * return yield* Ref.modify(state, (s) => [
446 * s.count,
447 * { count: s.count + 1, total: s.total + s.count + 1 }
448 * ])
449 * })
450 *
451 * await Effect.runPromise(program) // => ["Previous value was 10", 20]
452 * await Effect.runPromise(program2) // => 0
453 * ```
454 *
455 * @see {@link updateAndGet} for returning the new stored value
456 * @see {@link modifySome} for optionally updating while returning a separate result
457 *
458 * @category mutations
459 * @since 2.0.0
460 */
461export const modify = dual<
462 <A, B>(f: (a: A) => readonly [B, A]) => (self: Ref<A>) => Effect.Effect<B>,
463 <A, B>(self: Ref<A>, f: (a: A) => readonly [B, A]) => Effect.Effect<B>
464>(2, (self, f) =>
465 Effect.sync(() => {
466 const [b, a] = f(self.ref.current)
467 self.ref.current = a
468 return b
469 }))
470
471/**
472 * Computes a result atomically and optionally updates the value of the `Ref`.
473 *
474 * **When to use**
475 *
476 * Use to compute a return value while optionally updating a plain `Ref`.
477 *
478 * **Details**
479 *
480 * The callback receives the current value and returns `[result, nextValue]`,
481 * where `nextValue` is an `Option`. If `nextValue` is `Option.some(value)`,
482 * the `Ref` is updated to `value`; if it is `Option.none()`, the `Ref` is left
483 * unchanged. The returned effect always succeeds with `result`.
484 *
485 * **Example** (Conditionally modifying a value)
486 *
487 * ```ts import.meta.vitest
488 * import { Effect, Option, Ref } from "effect"
489 *
490 * const program = Effect.gen(function*() {
491 * const counter = yield* Ref.make(5)
492 *
493 * const result1 = yield* Ref.modifySome(
494 * counter,
495 * (n) =>
496 * n > 3
497 * ? [`incremented ${n}`, Option.some(n + 10)]
498 * : ["no change", Option.none()]
499 * )
500 * const current1 = yield* Ref.get(counter)
501 * const result2 = yield* Ref.modifySome(
502 * counter,
503 * (n) =>
504 * n < 10
505 * ? [`decremented ${n}`, Option.some(n - 5)]
506 * : ["no change", Option.none()]
507 * )
508 * const current2 = yield* Ref.get(counter)
509 * return [result1, current1, result2, current2]
510 * })
511 *
512 * await Effect.runPromise(program) // => ["incremented 5", 15, "no change", 15]
513 * ```
514 *
515 * @see {@link modify} for always storing a new value
516 * @see {@link updateSome} for optional updates without a separate return value
517 *
518 * @category mutations
519 * @since 2.0.0
520 */
521export const modifySome: {
522 <B, A>(pf: (a: A) => readonly [B, Option.Option<A>]): (self: Ref<A>) => Effect.Effect<B>
523 <A, B>(self: Ref<A>, pf: (a: A) => readonly [B, Option.Option<A>]): Effect.Effect<B>
524} = dual<
525 <B, A>(
526 pf: (a: A) => readonly [B, Option.Option<A>]
527 ) => (self: Ref<A>) => Effect.Effect<B>,
528 <A, B>(
529 self: Ref<A>,
530 pf: (a: A) => readonly [B, Option.Option<A>]
531 ) => Effect.Effect<B>
532>(2, (self, pf) =>
533 modify(self, (value) => {
534 const [b, option] = pf(value)
535 return [b, option._tag === "None" ? value : option.value]
536 }))
537
538/**
539 * Updates the value of the Ref atomically using the given function.
540 *
541 * **When to use**
542 *
543 * Use to apply a `Ref` state transition without returning a value.
544 *
545 * **Example** (Updating a value)
546 *
547 * ```ts import.meta.vitest
548 * import { Effect, Ref } from "effect"
549 *
550 * const program = Effect.gen(function*() {
551 * const counter = yield* Ref.make(5)
552 *
553 * yield* Ref.update(counter, (n) => n * 2)
554 * return yield* Ref.get(counter)
555 * })
556 *
557 * const program2 = Effect.gen(function*() {
558 * const counter = yield* Ref.make(5)
559 * yield* Ref.update(counter, (n: number) => n + 10)
560 * return yield* Ref.get(counter)
561 * })
562 *
563 * await Effect.runPromise(program) // => 10
564 * await Effect.runPromise(program2) // => 15
565 * ```
566 *
567 * @see {@link updateAndGet} for returning the new value
568 * @see {@link getAndUpdate} for returning the previous value
569 *
570 * @category mutations
571 * @since 2.0.0
572 */
573export const update = dual<
574 <A>(f: (a: A) => A) => (self: Ref<A>) => Effect.Effect<void>,
575 <A>(self: Ref<A>, f: (a: A) => A) => Effect.Effect<void>
576>(2, <A>(self: Ref<A>, f: (a: A) => A) =>
577 Effect.sync(() => {
578 self.ref.current = f(self.ref.current)
579 }))
580
581/**
582 * Updates the value of the Ref atomically using the given function and returns the new value.
583 *
584 * **When to use**
585 *
586 * Use to apply a `Ref` state transition and return the new stored value.
587 *
588 * **Example** (Updating and returning the new value)
589 *
590 * ```ts import.meta.vitest
591 * import { Effect, Ref } from "effect"
592 *
593 * const program = Effect.gen(function*() {
594 * const counter = yield* Ref.make(5)
595 *
596 * const newValue = yield* Ref.updateAndGet(counter, (n) => n * 3)
597 * const current = yield* Ref.get(counter)
598 * return [newValue, current]
599 * })
600 *
601 * await Effect.runPromise(program) // => [15, 15]
602 * ```
603 *
604 * @see {@link update} for updating without returning the new value
605 * @see {@link getAndUpdate} for returning the previous value instead
606 *
607 * @category mutations
608 * @since 2.0.0
609 */
610export const updateAndGet = dual<
611 <A>(f: (a: A) => A) => (self: Ref<A>) => Effect.Effect<A>,
612 <A>(self: Ref<A>, f: (a: A) => A) => Effect.Effect<A>
613>(2, <A>(self: Ref<A>, f: (a: A) => A) => Effect.sync(() => self.ref.current = f(self.ref.current)))
614
615/**
616 * Updates the value of the Ref atomically using the given partial function.
617 *
618 * **When to use**
619 *
620 * Use to apply a conditional `Ref` update without returning a value.
621 *
622 * **Details**
623 *
624 * If the partial function returns `Option.some`, the Ref is updated with the
625 * new value. If it returns `Option.none`, the Ref is left unchanged.
626 *
627 * **Example** (Conditionally updating a value)
628 *
629 * ```ts import.meta.vitest
630 * import { Effect, Option, Ref } from "effect"
631 *
632 * const program = Effect.gen(function*() {
633 * const counter = yield* Ref.make(5)
634 *
635 * yield* Ref.updateSome(
636 * counter,
637 * (n) => n % 2 === 0 ? Option.some(n * 2) : Option.none()
638 * )
639 * const before = yield* Ref.get(counter)
640 * yield* Ref.set(counter, 6)
641 * yield* Ref.updateSome(
642 * counter,
643 * (n) => n % 2 === 0 ? Option.some(n * 2) : Option.none()
644 * )
645 * const after = yield* Ref.get(counter)
646 * return [before, after]
647 * })
648 *
649 * await Effect.runPromise(program) // => [5, 12]
650 * ```
651 *
652 * @see {@link update} for always applying an update
653 * @see {@link updateSomeAndGet} for returning the resulting current value
654 *
655 * @category mutations
656 * @since 2.0.0
657 */
658export const updateSome = dual<
659 <A>(f: (a: A) => Option.Option<A>) => (self: Ref<A>) => Effect.Effect<void>,
660 <A>(self: Ref<A>, f: (a: A) => Option.Option<A>) => Effect.Effect<void>
661>(2, <A>(self: Ref<A>, f: (a: A) => Option.Option<A>) =>
662 Effect.sync(() => {
663 const option = f(self.ref.current)
664 if (option._tag === "Some") {
665 self.ref.current = option.value
666 }
667 }))
668
669/**
670 * Updates the value of the Ref atomically using the given partial function and returns the current value.
671 *
672 * **When to use**
673 *
674 * Use to apply a conditional `Ref` update and return the resulting current
675 * value.
676 *
677 * **Details**
678 *
679 * If the partial function returns `Option.some`, the Ref is updated with the
680 * new value. If it returns `Option.none`, the Ref is left unchanged. The effect
681 * returns the current value of the Ref after the potential update.
682 *
683 * **Example** (Conditionally updating and returning the current value)
684 *
685 * ```ts import.meta.vitest
686 * import { Effect, Option, Ref } from "effect"
687 *
688 * const program = Effect.gen(function*() {
689 * const counter = yield* Ref.make(10)
690 *
691 * const result1 = yield* Ref.updateSomeAndGet(
692 * counter,
693 * (n) => n > 5 ? Option.some(n / 2) : Option.none()
694 * )
695 * const result2 = yield* Ref.updateSomeAndGet(
696 * counter,
697 * (n) => n > 5 ? Option.some(n / 2) : Option.none()
698 * )
699 * return [result1, result2]
700 * })
701 *
702 * await Effect.runPromise(program) // => [5, 5]
703 * ```
704 *
705 * @see {@link updateSome} for conditional updates without returning a value
706 * @see {@link updateAndGet} for always updating and returning the new value
707 *
708 * @category mutations
709 * @since 2.0.0
710 */
711export const updateSomeAndGet = dual<
712 <A>(pf: (a: A) => Option.Option<A>) => (self: Ref<A>) => Effect.Effect<A>,
713 <A>(self: Ref<A>, pf: (a: A) => Option.Option<A>) => Effect.Effect<A>
714>(2, <A>(self: Ref<A>, pf: (a: A) => Option.Option<A>) =>
715 Effect.sync(() => {
716 const option = pf(self.ref.current)
717 if (option._tag === "Some") {
718 self.ref.current = option.value
719 }
720 return self.ref.current
721 }))
722
723/**
724 * Gets the current value of the Ref synchronously (unsafe version).
725 *
726 * **When to use**
727 *
728 * Use when you need immediate synchronous access and can guarantee that
729 * reading the `Ref` outside of `Effect` is safe.
730 *
731 * **Gotchas**
732 *
733 * Prefer `Ref.get` for Effect-wrapped access in Effect programs.
734 *
735 * **Example** (Reading a ref unsafely)
736 *
737 * ```ts import.meta.vitest
738 * import { Ref } from "effect"
739 *
740 * const counter = Ref.makeUnsafe(42)
741 * Ref.getUnsafe(counter) // => 42
742 * ```
743 *
744 * @category getters
745 * @since 4.0.0
746 */
747export const getUnsafe = <A>(self: Ref<A>): A => self.ref.current