1/**
2 * Stores mutable state and publishes changes as a stream.
3 *
4 * A `SubscriptionRef<A>` stores the latest value, publishes the initial value,
5 * and publishes every committed update so subscribers can observe state over
6 * time. Updates are serialized so only one change is applied at a time. This
7 * module includes constructors, current-value reads, the `changes` stream,
8 * writes, updates, partial updates, and effectful update helpers.
9 *
10 * @since 2.0.0
11 */
12import * as Effect from "./Effect.ts"
13import { dual, identity } from "./Function.ts"
14import { PipeInspectableProto } from "./internal/core.ts"
15import * as Option from "./Option.ts"
16import type { Pipeable } from "./Pipeable.ts"
17import { hasProperty } from "./Predicate.ts"
18import * as PubSub from "./PubSub.ts"
19import * as Semaphore from "./Semaphore.ts"
20import * as Stream from "./Stream.ts"
21import type { Invariant } from "./Types.ts"
22
23const TypeId = "~effect/SubscriptionRef"
24
25/**
26 * A mutable reference whose updates are serialized and published to
27 * subscribers.
28 *
29 * **When to use**
30 *
31 * Use to observe the current value and subsequent updates as a
32 * stream.
33 *
34 * @category models
35 * @since 2.0.0
36 */
37export interface SubscriptionRef<in out A> extends SubscriptionRef.Variance<A>, Pipeable {
38 value: A
39 readonly semaphore: Semaphore.Semaphore
40 readonly pubsub: PubSub.PubSub<A>
41}
42
43/**
44 * Returns `true` if the provided value is a `SubscriptionRef`.
45 *
46 * **When to use**
47 *
48 * Use to narrow an unknown value before calling `SubscriptionRef` operations
49 * that require a subscription reference.
50 *
51 * @category guards
52 * @since 4.0.0
53 */
54export const isSubscriptionRef: (u: unknown) => u is SubscriptionRef<unknown> = (
55 u: unknown
56): u is SubscriptionRef<unknown> => hasProperty(u, TypeId)
57
58/**
59 * The `SubscriptionRef` namespace containing type definitions associated with
60 * subscription references.
61 *
62 * @since 2.0.0
63 */
64export declare namespace SubscriptionRef {
65 /**
66 * Type-level variance marker for the value type carried by a
67 * `SubscriptionRef`.
68 *
69 * @category models
70 * @since 2.0.0
71 */
72 export interface Variance<in out A> {
73 readonly [TypeId]: {
74 readonly _A: Invariant<A>
75 }
76 }
77}
78
79const Proto = {
80 ...PipeInspectableProto,
81 [TypeId]: {
82 _A: identity
83 },
84 toJSON(this: SubscriptionRef<unknown>) {
85 return {
86 _id: "SubscriptionRef",
87 value: this.value
88 }
89 }
90}
91
92/**
93 * Constructs a new `SubscriptionRef` from an initial value.
94 *
95 * **When to use**
96 *
97 * Use to create a `SubscriptionRef` when consumers need to read the latest
98 * value and subscribe to every update.
99 *
100 * **Details**
101 *
102 * The initial value is published during construction, so `changes` starts new
103 * subscribers with that value before future updates.
104 *
105 * @see {@link changes} for streaming the current value and subsequent updates
106 * @see {@link set} for replacing the value and notifying subscribers
107 *
108 * @category constructors
109 * @since 2.0.0
110 */
111export const make = <A>(value: A): Effect.Effect<SubscriptionRef<A>> =>
112 Effect.map(PubSub.unbounded<A>({ replay: 1 }), (pubsub) => {
113 const self = Object.create(Proto)
114 self.semaphore = Semaphore.makeUnsafe(1)
115 self.value = value
116 self.pubsub = pubsub
117 PubSub.publishUnsafe(self.pubsub, value)
118 return self
119 })
120
121/**
122 * Creates a stream that emits the current value and all subsequent changes to
123 * the `SubscriptionRef`.
124 *
125 * **Details**
126 *
127 * The stream will first emit the current value, then emit all future changes
128 * as they occur.
129 *
130 * **Example** (Streaming changes)
131 *
132 * ```ts import.meta.vitest
133 * import { Deferred, Effect, Fiber, Stream, SubscriptionRef } from "effect"
134 *
135 * const program = Effect.gen(function*() {
136 * const ref = yield* SubscriptionRef.make(0)
137 * const ready = yield* Deferred.make<void>()
138 *
139 * const fiber = yield* SubscriptionRef.changes(ref).pipe(
140 * Stream.tap(() => Deferred.succeed(ready, void 0)),
141 * Stream.take(3),
142 * Stream.runCollect,
143 * Effect.forkChild
144 * )
145 *
146 * yield* Deferred.await(ready)
147 * yield* SubscriptionRef.set(ref, 1)
148 * yield* SubscriptionRef.set(ref, 2)
149 *
150 * const values = yield* Fiber.join(fiber)
151 * return Array.from(values)
152 * })
153 *
154 * await Effect.runPromise(program) // => [0, 1, 2]
155 * ```
156 *
157 * @category subscriptions
158 * @since 4.0.0
159 */
160export const changes = <A>(self: SubscriptionRef<A>): Stream.Stream<A> => Stream.fromPubSub(self.pubsub)
161
162/**
163 * Retrieves the current value of the `SubscriptionRef` unsafely.
164 *
165 * **When to use**
166 *
167 * Use when you are in synchronous internals or test setup where concurrent
168 * updates are controlled.
169 *
170 * **Gotchas**
171 *
172 * This function directly accesses the underlying reference without any
173 * synchronization. It should only be used when you are certain there are no
174 * concurrent modifications.
175 *
176 * **Example** (Reading the current value unsafely)
177 *
178 * ```ts import.meta.vitest
179 * import { Effect, SubscriptionRef } from "effect"
180 *
181 * const program = Effect.gen(function*() {
182 * const ref = yield* SubscriptionRef.make(42)
183 *
184 * return SubscriptionRef.getUnsafe(ref)
185 * })
186 *
187 * await Effect.runPromise(program) // => 42
188 * ```
189 *
190 * @category getters
191 * @since 4.0.0
192 */
193export const getUnsafe = <A>(self: SubscriptionRef<A>): A => self.value
194
195/**
196 * Retrieves the current value of the `SubscriptionRef`.
197 *
198 * **Example** (Reading the current value)
199 *
200 * ```ts import.meta.vitest
201 * import { Effect, SubscriptionRef } from "effect"
202 *
203 * const program = Effect.gen(function*() {
204 * const ref = yield* SubscriptionRef.make(42)
205 *
206 * return yield* SubscriptionRef.get(ref)
207 * })
208 *
209 * await Effect.runPromise(program) // => 42
210 * ```
211 *
212 * @category getters
213 * @since 2.0.0
214 */
215export const get = <A>(self: SubscriptionRef<A>): Effect.Effect<A> => Effect.sync(() => self.value)
216
217/**
218 * Retrieves the current value and sets a new value atomically, notifying
219 * subscribers of the change.
220 *
221 * **Example** (Getting and setting a value)
222 *
223 * ```ts import.meta.vitest
224 * import { Effect, SubscriptionRef } from "effect"
225 *
226 * const program = Effect.gen(function*() {
227 * const ref = yield* SubscriptionRef.make(10)
228 *
229 * const oldValue = yield* SubscriptionRef.getAndSet(ref, 20)
230 * const newValue = yield* SubscriptionRef.get(ref)
231 * return [oldValue, newValue]
232 * })
233 *
234 * await Effect.runPromise(program) // => [10, 20]
235 * ```
236 *
237 * @category getters
238 * @since 2.0.0
239 */
240export const getAndSet: {
241 <A>(value: A): (self: SubscriptionRef<A>) => Effect.Effect<A>
242 <A>(self: SubscriptionRef<A>, value: A): Effect.Effect<A>
243} = dual(2, <A>(self: SubscriptionRef<A>, value: A) =>
244 self.semaphore.withPermit(Effect.sync(() => {
245 const current = self.value
246 setUnsafe(self, value)
247 return current
248 })))
249
250const setUnsafe = <A>(self: SubscriptionRef<A>, value: A) => {
251 self.value = value
252 PubSub.publishUnsafe(self.pubsub, value)
253}
254
255/**
256 * Retrieves the current value and updates it atomically with the result of
257 * applying a function, notifying subscribers of the change.
258 *
259 * **Example** (Getting and updating a value)
260 *
261 * ```ts import.meta.vitest
262 * import { Effect, SubscriptionRef } from "effect"
263 *
264 * const program = Effect.gen(function*() {
265 * const ref = yield* SubscriptionRef.make(10)
266 *
267 * const oldValue = yield* SubscriptionRef.getAndUpdate(ref, (n) => n * 2)
268 * const newValue = yield* SubscriptionRef.get(ref)
269 * return [oldValue, newValue]
270 * })
271 *
272 * await Effect.runPromise(program) // => [10, 20]
273 * ```
274 *
275 * @category getters
276 * @since 2.0.0
277 */
278export const getAndUpdate: {
279 <A>(update: (a: A) => A): (self: SubscriptionRef<A>) => Effect.Effect<A>
280 <A>(self: SubscriptionRef<A>, update: (a: A) => A): Effect.Effect<A>
281} = dual(2, <A>(self: SubscriptionRef<A>, update: (a: A) => A) =>
282 self.semaphore.withPermit(Effect.sync(() => {
283 const current = self.value
284 const newValue = update(current)
285 setUnsafe(self, newValue)
286 return current
287 })))
288
289/**
290 * Retrieves the current value and updates it atomically with the result of
291 * applying an effectful function, notifying subscribers of the change.
292 *
293 * **Example** (Getting and updating with an effect)
294 *
295 * ```ts import.meta.vitest
296 * import { Effect, SubscriptionRef } from "effect"
297 *
298 * const program = Effect.gen(function*() {
299 * const ref = yield* SubscriptionRef.make(10)
300 *
301 * const oldValue = yield* SubscriptionRef.getAndUpdateEffect(
302 * ref,
303 * (n) => Effect.succeed(n + 5)
304 * )
305 * const newValue = yield* SubscriptionRef.get(ref)
306 * return [oldValue, newValue]
307 * })
308 *
309 * await Effect.runPromise(program) // => [10, 15]
310 * ```
311 *
312 * @category getters
313 * @since 2.0.0
314 */
315export const getAndUpdateEffect: {
316 <A, E, R>(update: (a: A) => Effect.Effect<A, E, R>): (self: SubscriptionRef<A>) => Effect.Effect<A, E, R>
317 <A, E, R>(self: SubscriptionRef<A>, update: (a: A) => Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
318} = dual(2, <A, E, R>(
319 self: SubscriptionRef<A>,
320 update: (a: A) => Effect.Effect<A, E, R>
321) =>
322 self.semaphore.withPermit(Effect.suspend(() => {
323 const current = self.value
324 return Effect.map(update(current), (newValue) => {
325 setUnsafe(self, newValue)
326 return current
327 })
328 })))
329
330/**
331 * Retrieves the current value and optionally updates the reference.
332 *
333 * **When to use**
334 *
335 * Use to read the old `SubscriptionRef` value while applying a synchronous
336 * update only when a new value is available.
337 *
338 * **Details**
339 *
340 * If the function returns `Option.some`, the new value is set and published. If
341 * it returns `Option.none`, the reference is left unchanged and no update is
342 * published.
343 *
344 * **Example** (Getting and conditionally updating a value)
345 *
346 * ```ts import.meta.vitest
347 * import { Effect, Option, SubscriptionRef } from "effect"
348 *
349 * const program = Effect.gen(function*() {
350 * const ref = yield* SubscriptionRef.make(10)
351 *
352 * const oldValue = yield* SubscriptionRef.getAndUpdateSome(
353 * ref,
354 * (n) => n > 5 ? Option.some(n * 2) : Option.none()
355 * )
356 * const newValue = yield* SubscriptionRef.get(ref)
357 * return [oldValue, newValue]
358 * })
359 *
360 * await Effect.runPromise(program) // => [10, 20]
361 * ```
362 *
363 * @category getters
364 * @since 2.0.0
365 */
366export const getAndUpdateSome: {
367 <A>(update: (a: A) => Option.Option<A>): (self: SubscriptionRef<A>) => Effect.Effect<A>
368 <A>(self: SubscriptionRef<A>, update: (a: A) => Option.Option<A>): Effect.Effect<A>
369} = dual(2, <A>(
370 self: SubscriptionRef<A>,
371 update: (a: A) => Option.Option<A>
372) =>
373 self.semaphore.withPermit(Effect.sync(() => {
374 const current = self.value
375 const option = update(current)
376 if (Option.isNone(option)) {
377 return current
378 }
379 setUnsafe(self, option.value)
380 return current
381 })))
382
383/**
384 * Retrieves the current value and optionally updates the reference effectfully.
385 *
386 * **When to use**
387 *
388 * Use to read the old `SubscriptionRef` value while applying an effectful
389 * update only when a new value is available.
390 *
391 * **Details**
392 *
393 * If the effect succeeds with `Option.some`, the new value is set and
394 * published. If it succeeds with `Option.none`, the reference is left unchanged
395 * and no update is published.
396 *
397 * **Example** (Getting and conditionally updating with an effect)
398 *
399 * ```ts import.meta.vitest
400 * import { Effect, Option, SubscriptionRef } from "effect"
401 *
402 * const program = Effect.gen(function*() {
403 * const ref = yield* SubscriptionRef.make(10)
404 *
405 * const oldValue = yield* SubscriptionRef.getAndUpdateSomeEffect(
406 * ref,
407 * (n) => Effect.succeed(n > 5 ? Option.some(n + 3) : Option.none())
408 * )
409 * const newValue = yield* SubscriptionRef.get(ref)
410 * return [oldValue, newValue]
411 * })
412 *
413 * await Effect.runPromise(program) // => [10, 13]
414 * ```
415 *
416 * @category getters
417 * @since 2.0.0
418 */
419export const getAndUpdateSomeEffect: {
420 <A, R, E>(
421 update: (a: A) => Effect.Effect<Option.Option<A>, E, R>
422 ): (self: SubscriptionRef<A>) => Effect.Effect<A, E, R>
423 <A, R, E>(
424 self: SubscriptionRef<A>,
425 update: (a: A) => Effect.Effect<Option.Option<A>, E, R>
426 ): Effect.Effect<A, E, R>
427} = dual(2, <A, R, E>(
428 self: SubscriptionRef<A>,
429 update: (a: A) => Effect.Effect<Option.Option<A>, E, R>
430) =>
431 self.semaphore.withPermit(Effect.suspend(() => {
432 const current = self.value
433 return Effect.map(update(current), (option) => {
434 if (Option.isNone(option)) return current
435 setUnsafe(self, option.value)
436 return current
437 })
438 })))
439
440/**
441 * Modifies the `SubscriptionRef` atomically with a function that computes a
442 * return value and a new value, notifying subscribers of the change.
443 *
444 * **Example** (Modifying a value)
445 *
446 * ```ts import.meta.vitest
447 * import { Effect, SubscriptionRef } from "effect"
448 *
449 * const program = Effect.gen(function*() {
450 * const ref = yield* SubscriptionRef.make(10)
451 *
452 * const result = yield* SubscriptionRef.modify(ref, (n) => [
453 * `Old value was ${n}`,
454 * n * 2
455 * ])
456 * const newValue = yield* SubscriptionRef.get(ref)
457 * return [result, newValue]
458 * })
459 *
460 * await Effect.runPromise(program) // => ["Old value was 10", 20]
461 * ```
462 *
463 * @category mutations
464 * @since 2.0.0
465 */
466export const modify: {
467 <A, B>(modify: (a: A) => readonly [B, A]): (self: SubscriptionRef<A>) => Effect.Effect<B>
468 <A, B>(self: SubscriptionRef<A>, f: (a: A) => readonly [B, A]): Effect.Effect<B>
469} = dual(2, <A, B>(
470 self: SubscriptionRef<A>,
471 modify: (a: A) => readonly [B, A]
472) =>
473 self.semaphore.withPermit(Effect.sync(() => {
474 const [b, newValue] = modify(self.value)
475 setUnsafe(self, newValue)
476 return b
477 })))
478
479/**
480 * Modifies the `SubscriptionRef` atomically with an effectful function that
481 * computes a return value and a new value, notifying subscribers of the
482 * change.
483 *
484 * **Example** (Modifying with an effect)
485 *
486 * ```ts import.meta.vitest
487 * import { Effect, SubscriptionRef } from "effect"
488 *
489 * const program = Effect.gen(function*() {
490 * const ref = yield* SubscriptionRef.make(10)
491 *
492 * const result = yield* SubscriptionRef.modifyEffect(
493 * ref,
494 * (n) => Effect.succeed([`Doubled from ${n}`, n * 2] as const)
495 * )
496 * const newValue = yield* SubscriptionRef.get(ref)
497 * return [result, newValue]
498 * })
499 *
500 * await Effect.runPromise(program) // => ["Doubled from 10", 20]
501 * ```
502 *
503 * @category mutations
504 * @since 2.0.0
505 */
506export const modifyEffect: {
507 <B, A, E, R>(
508 modify: (a: A) => Effect.Effect<readonly [B, A], E, R>
509 ): (self: SubscriptionRef<A>) => Effect.Effect<B, E, R>
510 <A, B, E, R>(
511 self: SubscriptionRef<A>,
512 modify: (a: A) => Effect.Effect<readonly [B, A], E, R>
513 ): Effect.Effect<B, E, R>
514} = dual(2, <A, B, E, R>(
515 self: SubscriptionRef<A>,
516 modify: (a: A) => Effect.Effect<readonly [B, A], E, R>
517): Effect.Effect<B, E, R> =>
518 self.semaphore.withPermit(Effect.suspend(() =>
519 Effect.map(modify(self.value), ([b, newValue]) => {
520 setUnsafe(self, newValue)
521 return b
522 })
523 )))
524
525/**
526 * Computes a return value and optionally updates the reference.
527 *
528 * **When to use**
529 *
530 * Use to return a separate result while synchronously deciding whether to
531 * publish a new `SubscriptionRef` value.
532 *
533 * **Details**
534 *
535 * If the function returns `Option.some` for the new value, the value is set and
536 * published. If it returns `Option.none`, the reference is left unchanged and
537 * no update is published.
538 *
539 * **Example** (Conditionally modifying a value)
540 *
541 * ```ts import.meta.vitest
542 * import { Effect, Option, SubscriptionRef } from "effect"
543 *
544 * const program = Effect.gen(function*() {
545 * const ref = yield* SubscriptionRef.make(10)
546 *
547 * const result = yield* SubscriptionRef.modifySome(
548 * ref,
549 * (n) =>
550 * n > 5 ? ["Updated", Option.some(n * 2)] : ["Not updated", Option.none()]
551 * )
552 * const newValue = yield* SubscriptionRef.get(ref)
553 * return [result, newValue]
554 * })
555 *
556 * await Effect.runPromise(program) // => ["Updated", 20]
557 * ```
558 *
559 * @category mutations
560 * @since 2.0.0
561 */
562export const modifySome: {
563 <B, A>(
564 modify: (a: A) => readonly [B, Option.Option<A>]
565 ): (self: SubscriptionRef<A>) => Effect.Effect<B>
566 <A, B>(
567 self: SubscriptionRef<A>,
568 modify: (a: A) => readonly [B, Option.Option<A>]
569 ): Effect.Effect<B>
570} = dual(2, <A, B>(
571 self: SubscriptionRef<A>,
572 modify: (a: A) => readonly [B, Option.Option<A>]
573) =>
574 self.semaphore.withPermit(Effect.sync(() => {
575 const [b, option] = modify(self.value)
576 if (Option.isNone(option)) return b
577 setUnsafe(self, option.value)
578 return b
579 })))
580
581/**
582 * Computes a return value and optionally updates the reference effectfully.
583 *
584 * **When to use**
585 *
586 * Use to return a separate result while effectfully deciding whether to publish
587 * a new `SubscriptionRef` value.
588 *
589 * **Details**
590 *
591 * If the effect succeeds with `Option.some`, the new value is set and
592 * published. If it succeeds with `Option.none`, the reference is left unchanged
593 * and no update is published.
594 *
595 * **Example** (Conditionally modifying with an effect)
596 *
597 * ```ts import.meta.vitest
598 * import { Effect, Option, SubscriptionRef } from "effect"
599 *
600 * const program = Effect.gen(function*() {
601 * const ref = yield* SubscriptionRef.make(10)
602 *
603 * const result = yield* SubscriptionRef.modifySomeEffect(
604 * ref,
605 * (n) =>
606 * Effect.succeed(
607 * n > 5
608 * ? (["Updated", Option.some(n + 5)] as const)
609 * : (["Not updated", Option.none()] as const)
610 * )
611 * )
612 * const newValue = yield* SubscriptionRef.get(ref)
613 * return [result, newValue]
614 * })
615 *
616 * await Effect.runPromise(program) // => ["Updated", 15]
617 * ```
618 *
619 * @category mutations
620 * @since 2.0.0
621 */
622export const modifySomeEffect: {
623 <A, B, R, E>(
624 modify: (a: A) => Effect.Effect<readonly [B, Option.Option<A>], E, R>
625 ): (self: SubscriptionRef<A>) => Effect.Effect<B, E, R>
626 <A, B, R, E>(
627 self: SubscriptionRef<A>,
628 modify: (a: A) => Effect.Effect<readonly [B, Option.Option<A>], E, R>
629 ): Effect.Effect<B, E, R>
630} = dual(2, <A, B, R, E>(
631 self: SubscriptionRef<A>,
632 modify: (a: A) => Effect.Effect<readonly [B, Option.Option<A>], E, R>
633) =>
634 self.semaphore.withPermit(Effect.suspend(() =>
635 Effect.map(modify(self.value), ([b, option]) => {
636 if (Option.isNone(option)) return b
637 setUnsafe(self, option.value)
638 return b
639 })
640 )))
641
642/**
643 * Sets the value of the `SubscriptionRef`, notifying all subscribers of the
644 * change.
645 *
646 * **Example** (Setting a value)
647 *
648 * ```ts import.meta.vitest
649 * import { Effect, SubscriptionRef } from "effect"
650 *
651 * const program = Effect.gen(function*() {
652 * const ref = yield* SubscriptionRef.make(0)
653 *
654 * yield* SubscriptionRef.set(ref, 42)
655 *
656 * return yield* SubscriptionRef.get(ref)
657 * })
658 *
659 * await Effect.runPromise(program) // => 42
660 * ```
661 *
662 * @category mutations
663 * @since 2.0.0
664 */
665export const set: {
666 <A>(value: A): (self: SubscriptionRef<A>) => Effect.Effect<void>
667 <A>(self: SubscriptionRef<A>, value: A): Effect.Effect<void>
668} = dual(
669 2,
670 <A>(self: SubscriptionRef<A>, value: A) => self.semaphore.withPermit(Effect.sync(() => setUnsafe(self, value)))
671)
672
673/**
674 * Sets the value of the `SubscriptionRef` and returns the new value,
675 * notifying all subscribers of the change.
676 *
677 * **Example** (Setting and reading the new value)
678 *
679 * ```ts import.meta.vitest
680 * import { Effect, SubscriptionRef } from "effect"
681 *
682 * const program = Effect.gen(function*() {
683 * const ref = yield* SubscriptionRef.make(0)
684 *
685 * return yield* SubscriptionRef.setAndGet(ref, 42)
686 * })
687 *
688 * await Effect.runPromise(program) // => 42
689 * ```
690 *
691 * @category mutations
692 * @since 2.0.0
693 */
694export const setAndGet: {
695 <A>(value: A): (self: SubscriptionRef<A>) => Effect.Effect<A>
696 <A>(self: SubscriptionRef<A>, value: A): Effect.Effect<A>
697} = dual(2, <A>(self: SubscriptionRef<A>, value: A) =>
698 self.semaphore.withPermit(Effect.sync(() => {
699 setUnsafe(self, value)
700 return value
701 })))
702
703/**
704 * Updates the value of the `SubscriptionRef` with the result of applying a
705 * function, notifying subscribers of the change.
706 *
707 * **Example** (Updating a value)
708 *
709 * ```ts import.meta.vitest
710 * import { Effect, SubscriptionRef } from "effect"
711 *
712 * const program = Effect.gen(function*() {
713 * const ref = yield* SubscriptionRef.make(10)
714 *
715 * yield* SubscriptionRef.update(ref, (n) => n * 2)
716 *
717 * return yield* SubscriptionRef.get(ref)
718 * })
719 *
720 * await Effect.runPromise(program) // => 20
721 * ```
722 *
723 * @category mutations
724 * @since 2.0.0
725 */
726export const update: {
727 <A>(update: (a: A) => A): (self: SubscriptionRef<A>) => Effect.Effect<void>
728 <A>(self: SubscriptionRef<A>, update: (a: A) => A): Effect.Effect<void>
729} = dual(
730 2,
731 <A>(self: SubscriptionRef<A>, update: (a: A) => A) =>
732 self.semaphore.withPermit(Effect.sync(() => setUnsafe(self, update(self.value))))
733)
734
735/**
736 * Updates the value of the `SubscriptionRef` with the result of applying an
737 * effectful function, notifying subscribers of the change.
738 *
739 * **Example** (Updating with an effect)
740 *
741 * ```ts import.meta.vitest
742 * import { Effect, SubscriptionRef } from "effect"
743 *
744 * const program = Effect.gen(function*() {
745 * const ref = yield* SubscriptionRef.make(10)
746 *
747 * yield* SubscriptionRef.updateEffect(ref, (n) => Effect.succeed(n + 5))
748 *
749 * return yield* SubscriptionRef.get(ref)
750 * })
751 *
752 * await Effect.runPromise(program) // => 15
753 * ```
754 *
755 * @category mutations
756 * @since 2.0.0
757 */
758export const updateEffect: {
759 <A, E, R>(update: (a: A) => Effect.Effect<A, E, R>): (self: SubscriptionRef<A>) => Effect.Effect<void, E, R>
760 <A, E, R>(self: SubscriptionRef<A>, update: (a: A) => Effect.Effect<A, E, R>): Effect.Effect<void, E, R>
761} = dual(2, <A, E, R>(
762 self: SubscriptionRef<A>,
763 update: (a: A) => Effect.Effect<A, E, R>
764) =>
765 self.semaphore.withPermit(
766 Effect.suspend(() => Effect.map(update(self.value), (newValue) => setUnsafe(self, newValue)))
767 ))
768
769/**
770 * Updates the value of the `SubscriptionRef` with the result of applying a
771 * function and returns the new value, notifying subscribers of the change.
772 *
773 * **Example** (Updating and reading the new value)
774 *
775 * ```ts import.meta.vitest
776 * import { Effect, SubscriptionRef } from "effect"
777 *
778 * const program = Effect.gen(function*() {
779 * const ref = yield* SubscriptionRef.make(10)
780 *
781 * return yield* SubscriptionRef.updateAndGet(ref, (n) => n * 2)
782 * })
783 *
784 * await Effect.runPromise(program) // => 20
785 * ```
786 *
787 * @category mutations
788 * @since 2.0.0
789 */
790export const updateAndGet: {
791 <A>(update: (a: A) => A): (self: SubscriptionRef<A>) => Effect.Effect<A>
792 <A>(self: SubscriptionRef<A>, update: (a: A) => A): Effect.Effect<A>
793} = dual(2, <A>(self: SubscriptionRef<A>, update: (a: A) => A) =>
794 self.semaphore.withPermit(Effect.sync(() => {
795 const newValue = update(self.value)
796 setUnsafe(self, newValue)
797 return newValue
798 })))
799
800/**
801 * Updates the value of the `SubscriptionRef` with the result of applying an
802 * effectful function and returns the new value, notifying subscribers of the
803 * change.
804 *
805 * **Example** (Updating with an effect and reading the new value)
806 *
807 * ```ts import.meta.vitest
808 * import { Effect, SubscriptionRef } from "effect"
809 *
810 * const program = Effect.gen(function*() {
811 * const ref = yield* SubscriptionRef.make(10)
812 *
813 * return yield* SubscriptionRef.updateAndGetEffect(
814 * ref,
815 * (n) => Effect.succeed(n + 5)
816 * )
817 * })
818 *
819 * await Effect.runPromise(program) // => 15
820 * ```
821 *
822 * @category mutations
823 * @since 2.0.0
824 */
825export const updateAndGetEffect: {
826 <A, E, R>(update: (a: A) => Effect.Effect<A, E, R>): (self: SubscriptionRef<A>) => Effect.Effect<A, E, R>
827 <A, E, R>(self: SubscriptionRef<A>, update: (a: A) => Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
828} = dual(2, <A, E, R>(
829 self: SubscriptionRef<A>,
830 update: (a: A) => Effect.Effect<A, E, R>
831) =>
832 self.semaphore.withPermit(Effect.suspend(() =>
833 Effect.map(update(self.value), (newValue) => {
834 setUnsafe(self, newValue)
835 return newValue
836 })
837 )))
838
839/**
840 * Applies an update function to the current value. If it returns
841 * `Option.some`, sets and publishes that value; if it returns `Option.none`,
842 * leaves the reference unchanged and does not publish.
843 *
844 * **Example** (Conditionally updating a value)
845 *
846 * ```ts import.meta.vitest
847 * import { Effect, Option, SubscriptionRef } from "effect"
848 *
849 * const program = Effect.gen(function*() {
850 * const ref = yield* SubscriptionRef.make(10)
851 *
852 * yield* SubscriptionRef.updateSome(
853 * ref,
854 * (n) => n > 5 ? Option.some(n * 2) : Option.none()
855 * )
856 *
857 * return yield* SubscriptionRef.get(ref)
858 * })
859 *
860 * await Effect.runPromise(program) // => 20
861 * ```
862 *
863 * @category mutations
864 * @since 2.0.0
865 */
866export const updateSome: {
867 <A>(update: (a: A) => Option.Option<A>): (self: SubscriptionRef<A>) => Effect.Effect<void>
868 <A>(self: SubscriptionRef<A>, update: (a: A) => Option.Option<A>): Effect.Effect<void>
869} = dual(2, <A>(
870 self: SubscriptionRef<A>,
871 update: (a: A) => Option.Option<A>
872) =>
873 self.semaphore.withPermit(Effect.sync(() => {
874 const option = update(self.value)
875 if (Option.isNone(option)) return
876 setUnsafe(self, option.value)
877 })))
878
879/**
880 * Applies an effectful update only when it produces a new value.
881 *
882 * **When to use**
883 *
884 * Use to conditionally update a `SubscriptionRef` with an effectful function
885 * while discarding the resulting value.
886 *
887 * **Details**
888 *
889 * If the effect succeeds with `Option.some`, the new value is set and
890 * published. If it succeeds with `Option.none`, the reference is left unchanged
891 * and no update is published.
892 *
893 * **Example** (Conditionally updating with an effect)
894 *
895 * ```ts import.meta.vitest
896 * import { Effect, Option, SubscriptionRef } from "effect"
897 *
898 * const program = Effect.gen(function*() {
899 * const ref = yield* SubscriptionRef.make(10)
900 *
901 * yield* SubscriptionRef.updateSomeEffect(
902 * ref,
903 * (n) => Effect.succeed(n > 5 ? Option.some(n + 3) : Option.none())
904 * )
905 *
906 * return yield* SubscriptionRef.get(ref)
907 * })
908 *
909 * await Effect.runPromise(program) // => 13
910 * ```
911 *
912 * @category mutations
913 * @since 2.0.0
914 */
915export const updateSomeEffect: {
916 <A, E, R>(
917 update: (a: A) => Effect.Effect<Option.Option<A>, E, R>
918 ): (self: SubscriptionRef<A>) => Effect.Effect<void, E, R>
919 <A, E, R>(
920 self: SubscriptionRef<A>,
921 update: (a: A) => Effect.Effect<Option.Option<A>, E, R>
922 ): Effect.Effect<void, E, R>
923} = dual(2, <A, R, E>(
924 self: SubscriptionRef<A>,
925 update: (a: A) => Effect.Effect<Option.Option<A>, E, R>
926) =>
927 self.semaphore.withPermit(Effect.suspend(() =>
928 Effect.map(update(self.value), (option) => {
929 if (Option.isNone(option)) return
930 setUnsafe(self, option.value)
931 })
932 )))
933
934/**
935 * Applies an optional update and returns the current value afterward.
936 *
937 * **When to use**
938 *
939 * Use to conditionally update a `SubscriptionRef` and read the value that is
940 * current after the update decision.
941 *
942 * **Details**
943 *
944 * If the function returns `Option.some`, the new value is set, published, and
945 * returned. If it returns `Option.none`, the unchanged current value is
946 * returned without publishing.
947 *
948 * **Example** (Conditionally updating and reading the new value)
949 *
950 * ```ts import.meta.vitest
951 * import { Effect, Option, SubscriptionRef } from "effect"
952 *
953 * const program = Effect.gen(function*() {
954 * const ref = yield* SubscriptionRef.make(10)
955 *
956 * return yield* SubscriptionRef.updateSomeAndGet(
957 * ref,
958 * (n) => n > 5 ? Option.some(n * 2) : Option.none()
959 * )
960 * })
961 *
962 * await Effect.runPromise(program) // => 20
963 * ```
964 *
965 * @category mutations
966 * @since 2.0.0
967 */
968export const updateSomeAndGet: {
969 <A>(update: (a: A) => Option.Option<A>): (self: SubscriptionRef<A>) => Effect.Effect<A>
970 <A>(self: SubscriptionRef<A>, update: (a: A) => Option.Option<A>): Effect.Effect<A>
971} = dual(2, <A>(
972 self: SubscriptionRef<A>,
973 update: (a: A) => Option.Option<A>
974) =>
975 self.semaphore.withPermit(Effect.sync(() => {
976 const current = self.value
977 const option = update(current)
978 if (Option.isNone(option)) return current
979 setUnsafe(self, option.value)
980 return option.value
981 })))
982
983/**
984 * Applies an effectful optional update and returns the current value afterward.
985 *
986 * **When to use**
987 *
988 * Use to conditionally update a `SubscriptionRef` effectfully and read the
989 * value that is current after the update decision.
990 *
991 * **Details**
992 *
993 * If the effect succeeds with `Option.some`, the new value is set, published,
994 * and returned. If it succeeds with `Option.none`, the unchanged current value
995 * is returned without publishing.
996 *
997 * **Example** (Conditionally updating with an effect and reading the new value)
998 *
999 * ```ts import.meta.vitest
1000 * import { Effect, Option, SubscriptionRef } from "effect"
1001 *
1002 * const program = Effect.gen(function*() {
1003 * const ref = yield* SubscriptionRef.make(10)
1004 *
1005 * return yield* SubscriptionRef.updateSomeAndGetEffect(
1006 * ref,
1007 * (n) => Effect.succeed(n > 5 ? Option.some(n + 3) : Option.none())
1008 * )
1009 * })
1010 *
1011 * await Effect.runPromise(program) // => 13
1012 * ```
1013 *
1014 * @category mutations
1015 * @since 2.0.0
1016 */
1017export const updateSomeAndGetEffect: {
1018 <A, E, R>(
1019 update: (a: A) => Effect.Effect<Option.Option<A>, E, R>
1020 ): (self: SubscriptionRef<A>) => Effect.Effect<A, E, R>
1021 <A, E, R>(self: SubscriptionRef<A>, update: (a: A) => Effect.Effect<Option.Option<A>, E, R>): Effect.Effect<A, E, R>
1022} = dual(2, <A, E, R>(
1023 self: SubscriptionRef<A>,
1024 update: (a: A) => Effect.Effect<Option.Option<A>, E, R>
1025) =>
1026 self.semaphore.withPermit(Effect.suspend(() => {
1027 const current = self.value
1028 return Effect.map(update(current), (option) => {
1029 if (Option.isNone(option)) return current
1030 setUnsafe(self, option.value)
1031 return option.value
1032 })
1033 })))