From 6e67d8ab7ccef5e1513427fadce7487ffbe3991d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Mon, 18 May 2026 14:12:48 +0200 Subject: [PATCH] Add provide --- packages/effect-lens/README.md | 14 ------- packages/effect-lens/src/Lens.test.ts | 5 +-- packages/effect-lens/src/Lens.ts | 54 ++++++++++++++++++++++++++- 3 files changed, 55 insertions(+), 18 deletions(-) diff --git a/packages/effect-lens/README.md b/packages/effect-lens/README.md index d177c27..c976d80 100644 --- a/packages/effect-lens/README.md +++ b/packages/effect-lens/README.md @@ -95,20 +95,6 @@ const lens = Effect.all([ Note: while Lens supports asynchronous effects for the proxy logic, we would recommend keeping them synchronous to preserve atomicity. -If a `Lens` depends on a service in its environment, you can provide that service directly to the lens: -```typescript -class Offset extends Context.Tag("Offset")() {} - -const root = Lens.fromSubscriptionRef(ref) -const offsetLens = Lens.mapEffect( - root, - n => Effect.map(Offset, ({ value }) => n + value), - (_n, next) => Effect.map(Offset, ({ value }) => next - value), -) - -const runnableLens = Lens.provide(offsetLens, Offset, { value: 5 }) -``` - ### Focusing diff --git a/packages/effect-lens/src/Lens.test.ts b/packages/effect-lens/src/Lens.test.ts index e417b42..c0707c3 100644 --- a/packages/effect-lens/src/Lens.test.ts +++ b/packages/effect-lens/src/Lens.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "bun:test" -import { Chunk, Context, Effect, Option, SubscriptionRef } from "effect" +import { Chunk, Context, Effect, Layer, Option, SubscriptionRef } from "effect" import * as Lens from "./Lens.js" @@ -67,8 +67,7 @@ describe("Lens", () => { n => Effect.map(Offset, ({ value }) => n + value), (_n, next) => Effect.map(Offset, ({ value }) => next - value), ), - Offset, - { value: 5 }, + Layer.succeed(Offset, { value: 5 }), ) return Effect.flatMap( diff --git a/packages/effect-lens/src/Lens.ts b/packages/effect-lens/src/Lens.ts index 9c6663b..7ad71fe 100644 --- a/packages/effect-lens/src/Lens.ts +++ b/packages/effect-lens/src/Lens.ts @@ -1,4 +1,4 @@ -import { Array, Chunk, type Context, Effect, Function, identity, Option, Pipeable, Predicate, Readable, Stream, type SubscriptionRef, type SynchronizedRef } from "effect" +import { Array, Chunk, Effect, Function, identity, type Layer, Option, Pipeable, Predicate, Readable, Stream, type Context, type SubscriptionRef, type SynchronizedRef } from "effect" import type { NoSuchElementException } from "effect/Cause" import * as Subscribable from "./Subscribable.js" @@ -273,6 +273,58 @@ export const mapStream: { * Provides a single service to a `Lens`, removing it from both the read and write environments. */ export const provide: { + ]>( + layers: Layers, + ): (self: Lens) => Lens< + A, + ER | { [k in keyof Layers]: Layer.Layer.Error }[number], + EW | { [k in keyof Layers]: Layer.Layer.Error }[number], + { [k in keyof Layers]: Layer.Layer.Context }[number] | Exclude }[number]>, + { [k in keyof Layers]: Layer.Layer.Context }[number] | Exclude }[number]> + > + ( + layer: Layer.Layer, + ): (self: Lens) => Lens, RIn | Exclude> + ( + context: Context.Context, + ): (self: Lens) => Lens, Exclude> + ]>( + self: Lens, + layers: Layers, + ): Lens< + A, + ER | { [k in keyof Layers]: Layer.Layer.Error }[number], + EW | { [k in keyof Layers]: Layer.Layer.Error }[number], + { [k in keyof Layers]: Layer.Layer.Context }[number] | Exclude }[number]>, + { [k in keyof Layers]: Layer.Layer.Context }[number] | Exclude }[number]> + > + ( + self: Lens, + layer: Layer.Layer, + ): Lens, RIn | Exclude> + ( + self: Lens, + context: Context.Context, + ): Lens, Exclude> +} = Function.dual(2, ( + self: Lens, + provider: unknown, +): Lens => make({ + get get() { return Effect.provide(self.get, provider as any) }, + get changes() { + return Stream.unwrapScoped( + Effect.map( + Effect.provide(Effect.context(), provider as any), + (context) => Stream.provideContext(self.changes as Stream.Stream, context), + ), + ) + }, + modify: ( + f: (a: A) => Effect.Effect + ) => Effect.provide(self.modify(f), provider as any), +})) + +export const provideService: { ( self: Lens, tag: Context.Tag,