Add provide
This commit is contained in:
@@ -95,6 +95,20 @@ const lens = Effect.all([
|
|||||||
|
|
||||||
Note: while Lens supports asynchronous effects for the proxy logic, we would recommend keeping them synchronous to preserve atomicity.
|
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")<Offset, { readonly value: number }>() {}
|
||||||
|
|
||||||
|
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
|
### Focusing
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import { describe, expect, test } from "bun:test"
|
import { describe, expect, test } from "bun:test"
|
||||||
import { Chunk, Effect, Option, SubscriptionRef } from "effect"
|
import { Chunk, Context, Effect, Option, SubscriptionRef } from "effect"
|
||||||
import * as Lens from "./Lens.js"
|
import * as Lens from "./Lens.js"
|
||||||
|
|
||||||
|
|
||||||
describe("Lens", () => {
|
describe("Lens", () => {
|
||||||
|
class Offset extends Context.Tag("Offset")<Offset, { readonly value: number }>() {}
|
||||||
|
|
||||||
test("mapOption transforms Some values and preserves None", async () => {
|
test("mapOption transforms Some values and preserves None", async () => {
|
||||||
const result = await Effect.runPromise(
|
const result = await Effect.runPromise(
|
||||||
Effect.flatMap(
|
Effect.flatMap(
|
||||||
@@ -54,6 +56,36 @@ describe("Lens", () => {
|
|||||||
expect(result[1]).toEqual(Option.some(50)) // 100 / 2
|
expect(result[1]).toEqual(Option.some(50)) // 100 / 2
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("provide supplies a service to get and modify", async () => {
|
||||||
|
const result = await Effect.runPromise(
|
||||||
|
Effect.flatMap(
|
||||||
|
SubscriptionRef.make(10),
|
||||||
|
parent => {
|
||||||
|
const lens = Lens.provide(
|
||||||
|
Lens.mapEffect(
|
||||||
|
Lens.fromSubscriptionRef(parent),
|
||||||
|
n => Effect.map(Offset, ({ value }) => n + value),
|
||||||
|
(_n, next) => Effect.map(Offset, ({ value }) => next - value),
|
||||||
|
),
|
||||||
|
Offset,
|
||||||
|
{ value: 5 },
|
||||||
|
)
|
||||||
|
|
||||||
|
return Effect.flatMap(
|
||||||
|
Lens.get(lens),
|
||||||
|
value => Effect.flatMap(
|
||||||
|
Lens.set(lens, 30),
|
||||||
|
() => Effect.map(parent.get, parentValue => [value, parentValue] as const),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result[0]).toBe(15)
|
||||||
|
expect(result[1]).toBe(25)
|
||||||
|
})
|
||||||
|
|
||||||
test("focusObjectOn focuses a nested property without touching other fields", async () => {
|
test("focusObjectOn focuses a nested property without touching other fields", async () => {
|
||||||
const [initialCount, updatedState] = await Effect.runPromise(
|
const [initialCount, updatedState] = await Effect.runPromise(
|
||||||
Effect.flatMap(
|
Effect.flatMap(
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Array, Chunk, Effect, Function, identity, Option, Pipeable, Predicate, Readable, Stream, type SubscriptionRef, type SynchronizedRef } from "effect"
|
import { Array, Chunk, type Context, Effect, Function, identity, Option, Pipeable, Predicate, Readable, Stream, type SubscriptionRef, type SynchronizedRef } from "effect"
|
||||||
import type { NoSuchElementException } from "effect/Cause"
|
import type { NoSuchElementException } from "effect/Cause"
|
||||||
import * as Subscribable from "./Subscribable.js"
|
import * as Subscribable from "./Subscribable.js"
|
||||||
|
|
||||||
@@ -269,6 +269,31 @@ export const mapStream: {
|
|||||||
get modify() { return self.modify },
|
get modify() { return self.modify },
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a single service to a `Lens`, removing it from both the read and write environments.
|
||||||
|
*/
|
||||||
|
export const provide: {
|
||||||
|
<A, ER, EW, RR, RW, I, S>(
|
||||||
|
self: Lens<A, ER, EW, RR, RW>,
|
||||||
|
tag: Context.Tag<I, S>,
|
||||||
|
service: NoInfer<S>,
|
||||||
|
): Lens<A, ER, EW, Exclude<RR, I>, Exclude<RW, I>>
|
||||||
|
<I, S>(
|
||||||
|
tag: Context.Tag<I, S>,
|
||||||
|
service: NoInfer<S>,
|
||||||
|
): <A, ER, EW, RR, RW>(self: Lens<A, ER, EW, RR, RW>) => Lens<A, ER, EW, Exclude<RR, I>, Exclude<RW, I>>
|
||||||
|
} = Function.dual(3, <A, ER, EW, RR, RW, I, S>(
|
||||||
|
self: Lens<A, ER, EW, RR, RW>,
|
||||||
|
tag: Context.Tag<I, S>,
|
||||||
|
service: NoInfer<S>,
|
||||||
|
): Lens<A, ER, EW, Exclude<RR, I>, Exclude<RW, I>> => make({
|
||||||
|
get get() { return Effect.provideService(self.get, tag, service) },
|
||||||
|
get changes() { return Stream.provideService(self.changes, tag, service) },
|
||||||
|
modify: <B, E1 = never, R1 = never>(
|
||||||
|
f: (a: A) => Effect.Effect<readonly [B, A], E1, R1>
|
||||||
|
) => Effect.provideService(self.modify(f), tag, service),
|
||||||
|
}))
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Narrows the focus to a field of an object. Replaces the object in an immutable fashion when written to.
|
* Narrows the focus to a field of an object. Replaces the object in an immutable fashion when written to.
|
||||||
|
|||||||
Reference in New Issue
Block a user