/** * @since 2.0.0 */ import * as Effect from "./Effect.ts" import { dual } from "./Function.ts" import { PipeInspectableProto } from "./internal/core.ts" import * as Option from "./Option.ts" import * as Ref from "./Ref.ts" import * as Semaphore from "./Semaphore.ts" const TypeId = "~effect/SynchronizedRef" /** * @since 2.0.0 * @category models */ export interface SynchronizedRef extends Ref.Ref { readonly [TypeId]: typeof TypeId readonly backing: Ref.Ref readonly semaphore: Semaphore.Semaphore } const Proto = { ...PipeInspectableProto, [TypeId]: TypeId, toJSON(this: SynchronizedRef) { return { _id: "SynchronizedRef", value: this.backing.ref.current } } } /** * @since 4.0.0 * @category constructors */ export const makeUnsafe = (value: A): SynchronizedRef => { const self = Object.create(Proto) self.semaphore = Semaphore.makeUnsafe(1) self.backing = Ref.makeUnsafe(value) return self } /** * @since 2.0.0 * @category constructors */ export const make = (value: A): Effect.Effect> => Effect.sync(() => makeUnsafe(value)) /** * @since 2.0.0 * @category getters */ export const getUnsafe = (self: SynchronizedRef): A => self.backing.ref.current /** * @since 2.0.0 * @category getters */ export const get = (self: SynchronizedRef): Effect.Effect => Effect.sync(() => getUnsafe(self)) /** * @since 2.0.0 * @category utils */ export const getAndSet: { /** * @since 2.0.0 * @category utils */ (value: A): (self: SynchronizedRef) => Effect.Effect /** * @since 2.0.0 * @category utils */ (self: SynchronizedRef, value: A): Effect.Effect } = dual( 2, (self: SynchronizedRef, value: A): Effect.Effect => self.semaphore.withPermit(Ref.getAndSet(self.backing, value)) ) /** * @since 2.0.0 * @category utils */ export const getAndUpdate: { /** * @since 2.0.0 * @category utils */ (f: (a: A) => A): (self: SynchronizedRef) => Effect.Effect /** * @since 2.0.0 * @category utils */ (self: SynchronizedRef, f: (a: A) => A): Effect.Effect } = dual( 2, (self: SynchronizedRef, f: (a: A) => A): Effect.Effect => self.semaphore.withPermit(Ref.getAndUpdate(self.backing, f)) ) /** * @since 2.0.0 * @category utils */ export const getAndUpdateEffect: { /** * @since 2.0.0 * @category utils */ (f: (a: A) => Effect.Effect): (self: SynchronizedRef) => Effect.Effect /** * @since 2.0.0 * @category utils */ (self: SynchronizedRef, f: (a: A) => Effect.Effect): Effect.Effect } = dual( 2, (self: SynchronizedRef, f: (a: A) => Effect.Effect): Effect.Effect => self.semaphore.withPermit(Effect.suspend(() => { const value = getUnsafe(self) return Effect.map(f(value), (newValue) => { self.backing.ref.current = newValue return value }) })) ) /** * @since 2.0.0 * @category utils */ export const getAndUpdateSome: { /** * @since 2.0.0 * @category utils */ (pf: (a: A) => Option.Option): (self: SynchronizedRef) => Effect.Effect /** * @since 2.0.0 * @category utils */ (self: SynchronizedRef, pf: (a: A) => Option.Option): Effect.Effect } = dual( 2, (self: SynchronizedRef, pf: (a: A) => Option.Option): Effect.Effect => self.semaphore.withPermit(Ref.getAndUpdateSome(self, pf)) ) /** * @since 2.0.0 * @category utils */ export const getAndUpdateSomeEffect: { /** * @since 2.0.0 * @category utils */ (pf: (a: A) => Effect.Effect, E, R>): (self: SynchronizedRef) => Effect.Effect /** * @since 2.0.0 * @category utils */ ( self: SynchronizedRef, pf: (a: A) => Effect.Effect, E, R> ): Effect.Effect } = dual( 2, (self: SynchronizedRef, pf: (a: A) => Effect.Effect, E, R>): Effect.Effect => self.semaphore.withPermit(Effect.suspend(() => { const value = getUnsafe(self) return Effect.flatMap(pf(value), (option) => { if (Option.isNone(option)) { return Effect.succeed(value) } self.backing.ref.current = option.value return Effect.succeed(value) }) })) ) /** * @since 2.0.0 * @category utils */ export const modify: { /** * @since 2.0.0 * @category utils */ (f: (a: A) => readonly [B, A]): (self: SynchronizedRef) => Effect.Effect /** * @since 2.0.0 * @category utils */ (self: SynchronizedRef, f: (a: A) => readonly [B, A]): Effect.Effect } = dual( 2, (self: SynchronizedRef, f: (a: A) => readonly [B, A]): Effect.Effect => self.semaphore.withPermit(Ref.modify(self.backing, f)) ) /** * @since 2.0.0 * @category utils */ export const modifyEffect: { /** * @since 2.0.0 * @category utils */ (f: (a: A) => Effect.Effect): (self: SynchronizedRef) => Effect.Effect /** * @since 2.0.0 * @category utils */ ( self: SynchronizedRef, f: (a: A) => Effect.Effect ): Effect.Effect } = dual( 2, (self: SynchronizedRef, f: (a: A) => Effect.Effect): Effect.Effect => self.semaphore.withPermit(Effect.suspend(() => { const value = getUnsafe(self) return Effect.map(f(value), ([b, a]) => { self.backing.ref.current = a return b }) })) ) /** * @since 2.0.0 * @category utils */ export const modifySome: { /** * @since 2.0.0 * @category utils */ (pf: (a: A) => readonly [B, Option.Option]): (self: SynchronizedRef) => Effect.Effect /** * @since 2.0.0 * @category utils */ (self: SynchronizedRef, pf: (a: A) => readonly [B, Option.Option]): Effect.Effect } = dual( 2, ( self: SynchronizedRef, pf: (a: A) => readonly [B, Option.Option] ): Effect.Effect => self.semaphore.withPermit(Ref.modifySome(self.backing, pf)) ) /** * @since 2.0.0 * @category utils */ export const modifySomeEffect: { /** * @since 2.0.0 * @category utils */ ( fallback: B, pf: (a: A) => Effect.Effect], E, R> ): (self: SynchronizedRef) => Effect.Effect /** * @since 2.0.0 * @category utils */ ( self: SynchronizedRef, pf: (a: A) => Effect.Effect], E, R> ): Effect.Effect } = dual( 2, ( self: SynchronizedRef, pf: (a: A) => Effect.Effect], E, R> ): Effect.Effect => self.semaphore.withPermit(Effect.suspend(() => { const value = getUnsafe(self) return Effect.flatMap(pf(value), ([b, maybeA]) => { if (Option.isNone(maybeA)) { return Effect.succeed(b) } self.backing.ref.current = maybeA.value return Effect.succeed(b) }) })) ) /** * @since 2.0.0 * @category utils */ export const set: { /** * @since 2.0.0 * @category utils */ (value: A): (self: SynchronizedRef) => Effect.Effect /** * @since 2.0.0 * @category utils */ (self: SynchronizedRef, value: A): Effect.Effect } = dual( 2, (self: SynchronizedRef, value: A): Effect.Effect => self.semaphore.withPermit(Ref.set(self.backing, value)) ) /** * @since 2.0.0 * @category utils */ export const setAndGet: { /** * @since 2.0.0 * @category utils */ (value: A): (self: SynchronizedRef) => Effect.Effect /** * @since 2.0.0 * @category utils */ (self: SynchronizedRef, value: A): Effect.Effect } = dual( 2, (self: SynchronizedRef, value: A): Effect.Effect => self.semaphore.withPermit(Ref.setAndGet(self.backing, value)) ) /** * @since 2.0.0 * @category utils */ export const update: { /** * @since 2.0.0 * @category utils */ (f: (a: A) => A): (self: SynchronizedRef) => Effect.Effect /** * @since 2.0.0 * @category utils */ (self: SynchronizedRef, f: (a: A) => A): Effect.Effect } = dual( 2, (self: SynchronizedRef, f: (a: A) => A): Effect.Effect => self.semaphore.withPermit(Ref.update(self.backing, f)) ) /** * @since 2.0.0 * @category utils */ export const updateEffect: { /** * @since 2.0.0 * @category utils */ (f: (a: A) => Effect.Effect): (self: SynchronizedRef) => Effect.Effect /** * @since 2.0.0 * @category utils */ (self: SynchronizedRef, f: (a: A) => Effect.Effect): Effect.Effect } = dual( 2, (self: SynchronizedRef, f: (a: A) => Effect.Effect): Effect.Effect => self.semaphore.withPermit(Effect.suspend(() => { const value = getUnsafe(self) return Effect.map(f(value), (newValue) => { self.backing.ref.current = newValue }) })) ) /** * @since 2.0.0 * @category utils */ export const updateAndGet: { /** * @since 2.0.0 * @category utils */ (f: (a: A) => A): (self: SynchronizedRef) => Effect.Effect /** * @since 2.0.0 * @category utils */ (self: SynchronizedRef, f: (a: A) => A): Effect.Effect } = dual( 2, (self: SynchronizedRef, f: (a: A) => A): Effect.Effect => self.semaphore.withPermit(Ref.updateAndGet(self.backing, f)) ) /** * @since 2.0.0 * @category utils */ export const updateAndGetEffect: { /** * @since 2.0.0 * @category utils */ (f: (a: A) => Effect.Effect): (self: SynchronizedRef) => Effect.Effect /** * @since 2.0.0 * @category utils */ (self: SynchronizedRef, f: (a: A) => Effect.Effect): Effect.Effect } = dual( 2, (self: SynchronizedRef, f: (a: A) => Effect.Effect): Effect.Effect => self.semaphore.withPermit(Effect.suspend(() => { const value = getUnsafe(self) return Effect.map(f(value), (newValue) => { self.backing.ref.current = newValue return newValue }) })) ) /** * @since 2.0.0 * @category utils */ export const updateSome: { /** * @since 2.0.0 * @category utils */ (f: (a: A) => Option.Option): (self: SynchronizedRef) => Effect.Effect /** * @since 2.0.0 * @category utils */ (self: SynchronizedRef, f: (a: A) => Option.Option): Effect.Effect } = dual( 2, (self: SynchronizedRef, f: (a: A) => Option.Option): Effect.Effect => self.semaphore.withPermit(Ref.updateSome(self.backing, f)) ) /** * @since 2.0.0 * @category utils */ export const updateSomeEffect: { /** * @since 2.0.0 * @category utils */ (pf: (a: A) => Effect.Effect, E, R>): (self: SynchronizedRef) => Effect.Effect /** * @since 2.0.0 * @category utils */ ( self: SynchronizedRef, pf: (a: A) => Effect.Effect, E, R> ): Effect.Effect } = dual( 2, (self: SynchronizedRef, pf: (a: A) => Effect.Effect, E, R>): Effect.Effect => self.semaphore.withPermit(Effect.suspend(() => { const value = getUnsafe(self) return Effect.map(pf(value), (option) => { if (Option.isNone(option)) { return } self.backing.ref.current = option.value }) })) ) /** * @since 2.0.0 * @category utils */ export const updateSomeAndGet: { /** * @since 2.0.0 * @category utils */ (pf: (a: A) => Option.Option): (self: SynchronizedRef) => Effect.Effect /** * @since 2.0.0 * @category utils */ (self: SynchronizedRef, pf: (a: A) => Option.Option): Effect.Effect } = dual( 2, (self: SynchronizedRef, pf: (a: A) => Option.Option): Effect.Effect => self.semaphore.withPermit(Ref.updateSomeAndGet(self.backing, pf)) ) /** * @since 2.0.0 * @category utils */ export const updateSomeAndGetEffect: { /** * @since 2.0.0 * @category utils */ (pf: (a: A) => Effect.Effect, E, R>): (self: SynchronizedRef) => Effect.Effect /** * @since 2.0.0 * @category utils */ ( self: SynchronizedRef, pf: (a: A) => Effect.Effect, E, R> ): Effect.Effect } = dual( 2, (self: SynchronizedRef, pf: (a: A) => Effect.Effect, E, R>): Effect.Effect => self.semaphore.withPermit(Effect.suspend(() => { const value = getUnsafe(self) return Effect.flatMap(pf(value), (option) => { if (Option.isNone(option)) { return Effect.succeed(value) } self.backing.ref.current = option.value return Effect.succeed(option.value) }) })) )