1/**
2 * Stores mutable state whose updates run one at a time.
3 *
4 * A `SynchronizedRef<A>` behaves like a `Ref<A>` for reading and simple state
5 * storage, but update and modify operations are serialized so each change sees
6 * a consistent current value. This is especially useful when the next value is
7 * computed by an effect, because the effectful transition is still protected
8 * from concurrent updates. This module includes constructors, reads, writes,
9 * updates, partial updates, and effectful update helpers.
10 *
11 * @since 2.0.0
12 */
13import * as Effect from "./Effect.ts"
14import { dual } from "./Function.ts"
15import { PipeInspectableProto } from "./internal/core.ts"
16import * as Option from "./Option.ts"
17import type { Pipeable } from "./Pipeable.ts"
18import * as Ref from "./Ref.ts"
19import * as Semaphore from "./Semaphore.ts"
20
21const TypeId = "~effect/SynchronizedRef"
22
23/**
24 * A mutable reference whose update and modify operations are serialized with an
25 * internal semaphore, including effectful transformations.
26 *
27 * **When to use**
28 *
29 * Use when shared state may be updated by multiple fibers and each update,
30 * including effectful state transitions, must observe one current value and run
31 * one at a time.
32 *
33 * @see {@link Ref.Ref} for a plain `Ref` when updates do not need effectful synchronization
34 *
35 * @category models
36 * @since 2.0.0
37 */
38export interface SynchronizedRef<in out A> extends Pipeable {
39 readonly [TypeId]: typeof TypeId
40 readonly backing: Ref.Ref<A>
41 readonly semaphore: Semaphore.Semaphore
42}
43
44const Proto = {
45 ...PipeInspectableProto,
46 [TypeId]: TypeId,
47 toJSON(this: SynchronizedRef<any>) {
48 return {
49 _id: "SynchronizedRef",
50 value: this.backing.ref.current
51 }
52 }
53}
54
55/**
56 * Creates a `SynchronizedRef` synchronously from an initial value.
57 *
58 * **When to use**
59 *
60 * Use when you need synchronous `SynchronizedRef` construction outside an
61 * Effect workflow.
62 *
63 * @category constructors
64 * @since 4.0.0
65 */
66export const makeUnsafe = <A>(value: A): SynchronizedRef<A> => {
67 const self = Object.create(Proto)
68 self.semaphore = Semaphore.makeUnsafe(1)
69 self.backing = Ref.makeUnsafe(value)
70 return self
71}
72
73/**
74 * Creates a `SynchronizedRef` from an initial value, wrapped in an `Effect`.
75 *
76 * **When to use**
77 *
78 * Use to create a `SynchronizedRef` inside an Effect program when later updates
79 * may run effects and must be serialized.
80 *
81 * **Details**
82 *
83 * The returned effect constructs a fresh `SynchronizedRef` by delegating to
84 * `makeUnsafe` when the effect is evaluated.
85 *
86 * @see {@link makeUnsafe} for synchronous construction when the caller controls safe initialization
87 * @see {@link Ref.make} for a plain `Ref` when updates do not need effectful synchronization
88 *
89 * @category constructors
90 * @since 2.0.0
91 */
92export const make = <A>(value: A): Effect.Effect<SynchronizedRef<A>> => Effect.sync(() => makeUnsafe(value))
93
94/**
95 * Reads the current value synchronously, bypassing the `Effect` API and the
96 * ref's semaphore.
97 *
98 * **When to use**
99 *
100 * Use when you need immediate synchronous access to a `SynchronizedRef` value
101 * in low-level code that can safely read outside an `Effect`.
102 *
103 * @see {@link get} for the Effect-wrapped read when composing inside Effect programs
104 *
105 * @category getters
106 * @since 4.0.0
107 */
108export const getUnsafe = <A>(self: SynchronizedRef<A>): A => self.backing.ref.current
109
110/**
111 * Returns an `Effect` that reads the current value of the `SynchronizedRef`.
112 *
113 * **When to use**
114 *
115 * Use to read the current value of a `SynchronizedRef` inside an `Effect`
116 * program without changing it.
117 *
118 * @see {@link getUnsafe} for synchronous reads when the caller controls safe access outside `Effect`
119 *
120 * @category getters
121 * @since 2.0.0
122 */
123export const get = <A>(self: SynchronizedRef<A>): Effect.Effect<A> => Effect.sync(() => getUnsafe(self))
124
125/**
126 * Sets a new value atomically and returns the previous value, serialized by the
127 * ref's semaphore.
128 *
129 * **When to use**
130 *
131 * Use to replace a `SynchronizedRef` with a known value when the previous value
132 * is also needed.
133 *
134 * @see {@link set} for setting a value without returning the previous value
135 * @see {@link setAndGet} for setting a value and returning the new value
136 * @see {@link getAndUpdate} for deriving the new value from the current value
137 *
138 * @category mutations
139 * @since 2.0.0
140 */
141export const getAndSet: {
142 <A>(value: A): (self: SynchronizedRef<A>) => Effect.Effect<A>
143 <A>(self: SynchronizedRef<A>, value: A): Effect.Effect<A>
144} = dual(
145 2,
146 <A>(self: SynchronizedRef<A>, value: A): Effect.Effect<A> =>
147 self.semaphore.withPermit(Ref.getAndSet(self.backing, value))
148)
149
150/**
151 * Updates the current value atomically with a function and returns the previous
152 * value, serialized by the ref's semaphore.
153 *
154 * **When to use**
155 *
156 * Use to run a pure `SynchronizedRef` state update when the previous stored
157 * value is also needed.
158 *
159 * @see {@link update} for updating without returning a value
160 * @see {@link updateAndGet} for updating and returning the new value
161 * @see {@link getAndUpdateEffect} for effectful updates that return the previous value
162 *
163 * @category mutations
164 * @since 2.0.0
165 */
166export const getAndUpdate: {
167 <A>(f: (a: A) => A): (self: SynchronizedRef<A>) => Effect.Effect<A>
168 <A>(self: SynchronizedRef<A>, f: (a: A) => A): Effect.Effect<A>
169} = dual(
170 2,
171 <A>(self: SynchronizedRef<A>, f: (a: A) => A): Effect.Effect<A> =>
172 self.semaphore.withPermit(Ref.getAndUpdate(self.backing, f))
173)
174
175/**
176 * Runs an effectful update atomically while holding the ref's semaphore, sets
177 * the new value if the effect succeeds, and returns the previous value.
178 *
179 * **When to use**
180 *
181 * Use when you need an effectful `SynchronizedRef` state transition to return
182 * the previous stored value.
183 *
184 * @see {@link getAndUpdate} for pure updates that return the previous value
185 * @see {@link updateEffect} for effectful updates without returning a value
186 * @see {@link updateAndGetEffect} for effectful updates that return the new value
187 * @see {@link modifyEffect} for effectful updates with a custom return value
188 * @see {@link getAndUpdateSomeEffect} for conditional effectful updates that return the previous value
189 *
190 * @category mutations
191 * @since 2.0.0
192 */
193export const getAndUpdateEffect: {
194 <A, R, E>(f: (a: A) => Effect.Effect<A, E, R>): (self: SynchronizedRef<A>) => Effect.Effect<A, E, R>
195 <A, R, E>(self: SynchronizedRef<A>, f: (a: A) => Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
196} = dual(
197 2,
198 <A, R, E>(self: SynchronizedRef<A>, f: (a: A) => Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>
199 self.semaphore.withPermit(Effect.suspend(() => {
200 const value = getUnsafe(self)
201 return Effect.map(f(value), (newValue) => {
202 self.backing.ref.current = newValue
203 return value
204 })
205 }))
206)
207
208/**
209 * Applies a partial update atomically and returns the previous value. If the
210 * function returns `Option.some`, the ref is updated; if it returns
211 * `Option.none`, the ref is left unchanged.
212 *
213 * **When to use**
214 *
215 * Use to return the previous `SynchronizedRef` value while applying a pure
216 * conditional update.
217 *
218 * @see {@link getAndUpdate} for always applying a pure update
219 * @see {@link updateSome} for applying a pure conditional update without returning the previous value
220 *
221 * @category mutations
222 * @since 2.0.0
223 */
224export const getAndUpdateSome: {
225 <A>(pf: (a: A) => Option.Option<A>): (self: SynchronizedRef<A>) => Effect.Effect<A>
226 <A>(self: SynchronizedRef<A>, pf: (a: A) => Option.Option<A>): Effect.Effect<A>
227} = dual(
228 2,
229 <A>(self: SynchronizedRef<A>, pf: (a: A) => Option.Option<A>): Effect.Effect<A> =>
230 self.semaphore.withPermit(Ref.getAndUpdateSome(self.backing, pf))
231)
232
233/**
234 * Runs an effectful partial update atomically while holding the ref's semaphore
235 * and returns the previous value. `Option.some` updates the ref; `Option.none`
236 * leaves it unchanged.
237 *
238 * **When to use**
239 *
240 * Use to return the previous `SynchronizedRef` value while running an effectful
241 * conditional update.
242 *
243 * @see {@link getAndUpdateSome} for the pure conditional variant
244 * @see {@link updateSomeEffect} for effectful conditional updates without returning the previous value
245 *
246 * @category mutations
247 * @since 2.0.0
248 */
249export const getAndUpdateSomeEffect: {
250 <A, R, E>(pf: (a: A) => Effect.Effect<Option.Option<A>, E, R>): (self: SynchronizedRef<A>) => Effect.Effect<A, E, R>
251 <A, R, E>(self: SynchronizedRef<A>, pf: (a: A) => Effect.Effect<Option.Option<A>, E, R>): Effect.Effect<A, E, R>
252} = dual(
253 2,
254 <A, R, E>(self: SynchronizedRef<A>, pf: (a: A) => Effect.Effect<Option.Option<A>, E, R>): Effect.Effect<A, E, R> =>
255 self.semaphore.withPermit(Effect.suspend(() => {
256 const value = getUnsafe(self)
257 return Effect.flatMap(pf(value), (option) => {
258 if (Option.isNone(option)) {
259 return Effect.succeed(value)
260 }
261 self.backing.ref.current = option.value
262 return Effect.succeed(value)
263 })
264 }))
265)
266
267/**
268 * Computes a return value and a new ref value atomically, stores the new value,
269 * and returns the computed result.
270 *
271 * **When to use**
272 *
273 * Use to derive a separate result and the next stored `SynchronizedRef` value
274 * from the same current value in one serialized pure update.
275 *
276 * @see {@link modifyEffect} for effectfully deriving both the result and next stored value
277 * @see {@link modifySome} for deriving a result and optionally updating the stored value
278 * @see {@link updateAndGet} for returning the new stored value instead of a separate result
279 *
280 * @category mutations
281 * @since 2.0.0
282 */
283export const modify: {
284 <A, B>(f: (a: A) => readonly [B, A]): (self: SynchronizedRef<A>) => Effect.Effect<B>
285 <A, B>(self: SynchronizedRef<A>, f: (a: A) => readonly [B, A]): Effect.Effect<B>
286} = dual(
287 2,
288 <A, B>(self: SynchronizedRef<A>, f: (a: A) => readonly [B, A]): Effect.Effect<B> =>
289 self.semaphore.withPermit(Ref.modify(self.backing, f))
290)
291
292/**
293 * Runs an effectful modification atomically while holding the ref's semaphore,
294 * stores the new value if the effect succeeds, and returns the computed result.
295 *
296 * **When to use**
297 *
298 * Use to effectfully compute both a separate return value and the next stored
299 * `SynchronizedRef` value in one serialized update.
300 *
301 * @see {@link modify} for the pure variant
302 * @see {@link updateEffect} for effectfully storing a new value without a separate result
303 *
304 * @category mutations
305 * @since 2.0.0
306 */
307export const modifyEffect: {
308 <A, B, E, R>(f: (a: A) => Effect.Effect<readonly [B, A], E, R>): (self: SynchronizedRef<A>) => Effect.Effect<B, E, R>
309 <A, B, E, R>(self: SynchronizedRef<A>, f: (a: A) => Effect.Effect<readonly [B, A], E, R>): Effect.Effect<B, E, R>
310} = dual(
311 2,
312 <A, B, E, R>(self: SynchronizedRef<A>, f: (a: A) => Effect.Effect<readonly [B, A], E, R>): Effect.Effect<B, E, R> =>
313 self.semaphore.withPermit(Effect.suspend(() => {
314 const value = getUnsafe(self)
315 return Effect.map(f(value), ([b, a]) => {
316 self.backing.ref.current = a
317 return b
318 })
319 }))
320)
321
322/**
323 * Computes a return value and an optional new ref value atomically.
324 * `Option.some` updates the ref; `Option.none` leaves it unchanged.
325 *
326 * **When to use**
327 *
328 * Use to compute a return value while optionally updating a `SynchronizedRef`
329 * under its semaphore.
330 *
331 * @see {@link modify} for always storing a new value
332 * @see {@link updateSome} for optional updates without a separate return value
333 *
334 * @category mutations
335 * @since 2.0.0
336 */
337export const modifySome: {
338 <B, A>(
339 pf: (a: A) => readonly [B, Option.Option<A>]
340 ): (self: SynchronizedRef<A>) => Effect.Effect<B>
341 <A, B>(
342 self: SynchronizedRef<A>,
343 pf: (a: A) => readonly [B, Option.Option<A>]
344 ): Effect.Effect<B>
345} = dual(
346 2,
347 <A, B>(
348 self: SynchronizedRef<A>,
349 pf: (a: A) => readonly [B, Option.Option<A>]
350 ): Effect.Effect<B> => self.semaphore.withPermit(Ref.modifySome(self.backing, pf))
351)
352
353/**
354 * Runs an effectful modification atomically while holding the ref's semaphore.
355 * The effect computes a return value and an optional new ref value;
356 * `Option.some` updates the ref and `Option.none` leaves it unchanged.
357 *
358 * **When to use**
359 *
360 * Use to effectfully compute a return value while optionally updating the
361 * stored `SynchronizedRef` value.
362 *
363 * @see {@link modifySome} for the pure variant
364 * @see {@link updateSomeEffect} for effectful optional updates without a separate return value
365 *
366 * @category mutations
367 * @since 2.0.0
368 */
369export const modifySomeEffect: {
370 <A, B, R, E>(
371 pf: (a: A) => Effect.Effect<readonly [B, Option.Option<A>], E, R>
372 ): (self: SynchronizedRef<A>) => Effect.Effect<B, E, R>
373 <A, B, R, E>(
374 self: SynchronizedRef<A>,
375 pf: (a: A) => Effect.Effect<readonly [B, Option.Option<A>], E, R>
376 ): Effect.Effect<B, E, R>
377} = dual(
378 2,
379 <A, B, R, E>(
380 self: SynchronizedRef<A>,
381 pf: (a: A) => Effect.Effect<readonly [B, Option.Option<A>], E, R>
382 ): Effect.Effect<B, E, R> =>
383 self.semaphore.withPermit(Effect.suspend(() => {
384 const value = getUnsafe(self)
385 return Effect.flatMap(pf(value), ([b, maybeA]) => {
386 if (Option.isNone(maybeA)) {
387 return Effect.succeed(b)
388 }
389 self.backing.ref.current = maybeA.value
390 return Effect.succeed(b)
391 })
392 }))
393)
394
395/**
396 * Sets the value of the `SynchronizedRef`, serialized by the ref's semaphore.
397 *
398 * **When to use**
399 *
400 * Use to replace the current value of a `SynchronizedRef` with a known value
401 * while keeping the write serialized with other synchronized updates.
402 *
403 * @see {@link getAndSet} for replacing the value when the previous value is needed
404 * @see {@link setAndGet} for replacing the value when the new value should be returned
405 * @see {@link update} for deriving the next value from the current value
406 *
407 * @category mutations
408 * @since 2.0.0
409 */
410export const set: {
411 <A>(value: A): (self: SynchronizedRef<A>) => Effect.Effect<void>
412 <A>(self: SynchronizedRef<A>, value: A): Effect.Effect<void>
413} = dual(
414 2,
415 <A>(self: SynchronizedRef<A>, value: A): Effect.Effect<void> =>
416 self.semaphore.withPermit(Ref.set(self.backing, value))
417)
418
419/**
420 * Sets the value of the `SynchronizedRef` and returns the new value.
421 *
422 * **When to use**
423 *
424 * Use to replace the current `SynchronizedRef` value with a known value and
425 * return that new value.
426 *
427 * @see {@link set} for setting without returning a value
428 * @see {@link getAndSet} for setting while returning the previous value
429 *
430 * @category mutations
431 * @since 2.0.0
432 */
433export const setAndGet: {
434 <A>(value: A): (self: SynchronizedRef<A>) => Effect.Effect<A>
435 <A>(self: SynchronizedRef<A>, value: A): Effect.Effect<A>
436} = dual(
437 2,
438 <A>(self: SynchronizedRef<A>, value: A): Effect.Effect<A> =>
439 self.semaphore.withPermit(Ref.setAndGet(self.backing, value))
440)
441
442/**
443 * Updates the value of the `SynchronizedRef` with a function, serialized by the
444 * ref's semaphore.
445 *
446 * **When to use**
447 *
448 * Use to apply a pure state transition to a `SynchronizedRef` as a serialized
449 * `Effect`.
450 *
451 * @see {@link updateEffect} for effectfully deriving the next value
452 * @see {@link updateAndGet} for returning the new stored value
453 * @see {@link getAndUpdate} for returning the previous stored value
454 *
455 * @category mutations
456 * @since 2.0.0
457 */
458export const update: {
459 <A>(f: (a: A) => A): (self: SynchronizedRef<A>) => Effect.Effect<void>
460 <A>(self: SynchronizedRef<A>, f: (a: A) => A): Effect.Effect<void>
461} = dual(
462 2,
463 <A>(self: SynchronizedRef<A>, f: (a: A) => A): Effect.Effect<void> =>
464 self.semaphore.withPermit(Ref.update(self.backing, f))
465)
466
467/**
468 * Runs an effectful update while holding the ref's semaphore and stores the new
469 * value if the effect succeeds.
470 *
471 * **When to use**
472 *
473 * Use to run an effectful state transition on a `SynchronizedRef` when storing
474 * the new value is the only result you need.
475 *
476 * @see {@link update} for a pure state transition
477 * @see {@link getAndUpdateEffect} for returning the previous stored value
478 * @see {@link updateAndGetEffect} for returning the new stored value
479 * @see {@link modifyEffect} for returning a separate result while storing a new value
480 * @see {@link updateSomeEffect} for effectfully applying only some state transitions
481 *
482 * @category mutations
483 * @since 2.0.0
484 */
485export const updateEffect: {
486 <A, R, E>(f: (a: A) => Effect.Effect<A, E, R>): (self: SynchronizedRef<A>) => Effect.Effect<void, E, R>
487 <A, R, E>(self: SynchronizedRef<A>, f: (a: A) => Effect.Effect<A, E, R>): Effect.Effect<void, E, R>
488} = dual(
489 2,
490 <A, R, E>(self: SynchronizedRef<A>, f: (a: A) => Effect.Effect<A, E, R>): Effect.Effect<void, E, R> =>
491 self.semaphore.withPermit(Effect.suspend(() => {
492 const value = getUnsafe(self)
493 return Effect.map(f(value), (newValue) => {
494 self.backing.ref.current = newValue
495 })
496 }))
497)
498
499/**
500 * Updates the value of the `SynchronizedRef` with a function and returns the
501 * new value.
502 *
503 * **When to use**
504 *
505 * Use to apply a pure `SynchronizedRef` state transition and return the new
506 * stored value.
507 *
508 * @see {@link update} for updating without returning the new value
509 * @see {@link getAndUpdate} for updating while returning the previous value
510 *
511 * @category mutations
512 * @since 2.0.0
513 */
514export const updateAndGet: {
515 <A>(f: (a: A) => A): (self: SynchronizedRef<A>) => Effect.Effect<A>
516 <A>(self: SynchronizedRef<A>, f: (a: A) => A): Effect.Effect<A>
517} = dual(
518 2,
519 <A>(self: SynchronizedRef<A>, f: (a: A) => A): Effect.Effect<A> =>
520 self.semaphore.withPermit(Ref.updateAndGet(self.backing, f))
521)
522
523/**
524 * Runs an effectful update while holding the ref's semaphore, stores the new
525 * value if the effect succeeds, and returns that new value.
526 *
527 * **When to use**
528 *
529 * Use to run an effectful `SynchronizedRef` state transition and return the new
530 * stored value.
531 *
532 * @see {@link updateEffect} for effectful updates without returning the new value
533 * @see {@link updateAndGet} for the pure variant
534 *
535 * @category mutations
536 * @since 2.0.0
537 */
538export const updateAndGetEffect: {
539 <A, R, E>(f: (a: A) => Effect.Effect<A, E, R>): (self: SynchronizedRef<A>) => Effect.Effect<A, E, R>
540 <A, R, E>(self: SynchronizedRef<A>, f: (a: A) => Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
541} = dual(
542 2,
543 <A, R, E>(self: SynchronizedRef<A>, f: (a: A) => Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>
544 self.semaphore.withPermit(Effect.suspend(() => {
545 const value = getUnsafe(self)
546 return Effect.map(f(value), (newValue) => {
547 self.backing.ref.current = newValue
548 return newValue
549 })
550 }))
551)
552
553/**
554 * Applies a partial update to the current value. `Option.some` stores the new
555 * value; `Option.none` leaves the ref unchanged.
556 *
557 * **When to use**
558 *
559 * Use to apply a pure conditional `SynchronizedRef` update without returning a
560 * value.
561 *
562 * @see {@link update} for always applying a pure update
563 * @see {@link updateSomeAndGet} for returning the resulting current value
564 *
565 * @category mutations
566 * @since 2.0.0
567 */
568export const updateSome: {
569 <A>(f: (a: A) => Option.Option<A>): (self: SynchronizedRef<A>) => Effect.Effect<void>
570 <A>(self: SynchronizedRef<A>, f: (a: A) => Option.Option<A>): Effect.Effect<void>
571} = dual(
572 2,
573 <A>(self: SynchronizedRef<A>, f: (a: A) => Option.Option<A>): Effect.Effect<void> =>
574 self.semaphore.withPermit(Ref.updateSome(self.backing, f))
575)
576
577/**
578 * Runs an effectful partial update while holding the ref's semaphore.
579 * `Option.some` stores the new value; `Option.none` leaves the ref unchanged.
580 *
581 * **When to use**
582 *
583 * Use to run an effectful conditional `SynchronizedRef` update without
584 * returning a value.
585 *
586 * @see {@link updateSome} for the pure conditional variant
587 * @see {@link updateEffect} for effectful updates that always store a new value
588 *
589 * @category mutations
590 * @since 2.0.0
591 */
592export const updateSomeEffect: {
593 <A, R, E>(
594 pf: (a: A) => Effect.Effect<Option.Option<A>, E, R>
595 ): (self: SynchronizedRef<A>) => Effect.Effect<void, E, R>
596 <A, R, E>(self: SynchronizedRef<A>, pf: (a: A) => Effect.Effect<Option.Option<A>, E, R>): Effect.Effect<void, E, R>
597} = dual(
598 2,
599 <A, R, E>(self: SynchronizedRef<A>, pf: (a: A) => Effect.Effect<Option.Option<A>, E, R>): Effect.Effect<void, E, R> =>
600 self.semaphore.withPermit(Effect.suspend(() => {
601 const value = getUnsafe(self)
602 return Effect.map(pf(value), (option) => {
603 if (Option.isNone(option)) {
604 return
605 }
606 self.backing.ref.current = option.value
607 })
608 }))
609)
610
611/**
612 * Applies a partial update and returns the resulting current value.
613 * `Option.some` stores and returns the new value; `Option.none` returns the
614 * unchanged value.
615 *
616 * **When to use**
617 *
618 * Use to apply a pure conditional `SynchronizedRef` update and return the
619 * resulting current value.
620 *
621 * @see {@link updateSome} for conditional updates without returning a value
622 * @see {@link updateAndGet} for always applying a pure update and returning the new value
623 *
624 * @category mutations
625 * @since 2.0.0
626 */
627export const updateSomeAndGet: {
628 <A>(pf: (a: A) => Option.Option<A>): (self: SynchronizedRef<A>) => Effect.Effect<A>
629 <A>(self: SynchronizedRef<A>, pf: (a: A) => Option.Option<A>): Effect.Effect<A>
630} = dual(
631 2,
632 <A>(self: SynchronizedRef<A>, pf: (a: A) => Option.Option<A>): Effect.Effect<A> =>
633 self.semaphore.withPermit(Ref.updateSomeAndGet(self.backing, pf))
634)
635
636/**
637 * Runs an effectful partial update while holding the ref's semaphore and
638 * returns the resulting current value. `Option.some` stores and returns the new
639 * value; `Option.none` returns the unchanged value.
640 *
641 * **When to use**
642 *
643 * Use to run an effectful conditional `SynchronizedRef` update and return the
644 * resulting current value.
645 *
646 * @see {@link updateSomeEffect} for effectful conditional updates without returning a value
647 * @see {@link updateAndGetEffect} for effectful updates that always store and return a new value
648 *
649 * @category mutations
650 * @since 2.0.0
651 */
652export const updateSomeAndGetEffect: {
653 <A, R, E>(pf: (a: A) => Effect.Effect<Option.Option<A>, E, R>): (self: SynchronizedRef<A>) => Effect.Effect<A, E, R>
654 <A, R, E>(self: SynchronizedRef<A>, pf: (a: A) => Effect.Effect<Option.Option<A>, E, R>): Effect.Effect<A, E, R>
655} = dual(
656 2,
657 <A, R, E>(self: SynchronizedRef<A>, pf: (a: A) => Effect.Effect<Option.Option<A>, E, R>): Effect.Effect<A, E, R> =>
658 self.semaphore.withPermit(Effect.suspend(() => {
659 const value = getUnsafe(self)
660 return Effect.flatMap(pf(value), (option) => {
661 if (Option.isNone(option)) {
662 return Effect.succeed(value)
663 }
664 self.backing.ref.current = option.value
665 return Effect.succeed(option.value)
666 })
667 }))
668)