From 0494511b663903448f593ad8e0b854a98f0ef168 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 28 May 2026 21:07:41 +0200 Subject: [PATCH] Add Ref impl --- packages/effect-lens/src/Lens.ts | 38 ++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/packages/effect-lens/src/Lens.ts b/packages/effect-lens/src/Lens.ts index 9959aa1..3579b6a 100644 --- a/packages/effect-lens/src/Lens.ts +++ b/packages/effect-lens/src/Lens.ts @@ -175,6 +175,44 @@ export const derive: { ): Lens => new DerivedLensImpl(asLensImpl(self), source)) +export class RefLensImpl +extends LensImpl { + constructor( + readonly ref: Ref.Ref, + readonly semaphore: Effect.Semaphore, + ) { + super() + } + + get resolve(): Effect.Effect, never, never> { + return Effect.map( + Ref.get(this.ref), + value => ({ + value, + commit: next => Effect.flatMap( + next, + value => Ref.set(this.ref, value), + ), + }), + ) + } + get changes() { return Stream.unwrap(Effect.map(Ref.get(this.ref), Stream.make)) } + get withLock() { return this.semaphore.withPermits(1) } +} + +/** + * Creates a `Lens` that proxies a `Ref`. + * + * Note: since `Ref` does not provide any kind of reactivity mechanism, the produced `Lens` will be non-reactive. + * This means its `changes` stream will only emit the current value once when evaluated and nothing else. + */ +export const fromRef = Effect.fnUntraced(function* ( + ref: Ref.Ref +): Effect.fn.Return, never, never> { + return new RefLensImpl(ref, yield* Effect.makeSemaphore(1)) +}) + + export declare namespace SynchronizedRefLensImpl { export interface SynchronizedRefWithInternals extends SynchronizedRef.SynchronizedRef {