Add focusOption
This commit is contained in:
@@ -165,6 +165,7 @@ Currently available:
|
||||
| `focusTupleAt` | Focuses to an indexed entry of a readonly tuple. Replaces the parent tuple immutably when writing to the focused index | Immutable | |
|
||||
| `focusMutableTupleAt` | Focuses to an indexed entry of a mutable tuple. Mutates the parent tuple in place at the focused index | Mutable | Type-safe: will not allow you to mutate `readonly` tuples |
|
||||
| `focusChunkAt` | Focuses to an indexed entry of a `Chunk`. Replaces the parent `Chunk` immutably when writing to the focused element | Immutable | |
|
||||
| `focusOption` | Focuses to the value inside an `Option`. Wraps writes back into `Option.some` | Immutable | Reading or writing fails with `NoSuchElementException` when the parent option is `None` |
|
||||
|
||||
Also more to come!
|
||||
|
||||
|
||||
@@ -183,6 +183,47 @@ describe("Lens", () => {
|
||||
expect(Chunk.toReadonlyArray(updated)).toEqual([1, 2, 99])
|
||||
})
|
||||
|
||||
test("focusOption reads and writes the inner Some value", async () => {
|
||||
const result = await Effect.runPromise(
|
||||
Effect.flatMap(
|
||||
SubscriptionRef.make<Option.Option<number>>(Option.some(42)),
|
||||
parent => {
|
||||
const lens = Lens.focusOption(Lens.fromSubscriptionRef(parent))
|
||||
return Effect.flatMap(
|
||||
Lens.get(lens),
|
||||
value => Effect.flatMap(
|
||||
Lens.set(lens, 100),
|
||||
() => Effect.map(parent.get, parentValue => [value, parentValue] as const),
|
||||
),
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
expect(result[0]).toBe(42)
|
||||
expect(result[1]).toEqual(Option.some(100))
|
||||
})
|
||||
|
||||
test("focusOption fails when the parent option is None", async () => {
|
||||
const result = await Effect.runPromise(
|
||||
Effect.flatMap(
|
||||
SubscriptionRef.make<Option.Option<number>>(Option.none()),
|
||||
parent => {
|
||||
const lens = Lens.focusOption(Lens.fromSubscriptionRef(parent))
|
||||
return Effect.all([
|
||||
Effect.either(Lens.get(lens)),
|
||||
Effect.either(Lens.set(lens, 100)),
|
||||
parent.get,
|
||||
] as const)
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
expect(result[0]._tag).toBe("Left")
|
||||
expect(result[1]._tag).toBe("Left")
|
||||
expect(result[2]).toEqual(Option.none())
|
||||
})
|
||||
|
||||
// test("changes stream emits updates when lens mutates state", async () => {
|
||||
// const events = await Effect.runPromise(
|
||||
// Effect.flatMap(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Array, Chunk, Effect, Function, Option, Pipeable, Predicate, Readable, Stream, type SubscriptionRef, type SynchronizedRef } from "effect"
|
||||
import * as Cause from "effect/Cause"
|
||||
import type { NoSuchElementException } from "effect/Cause"
|
||||
import * as Subscribable from "./Subscribable.js"
|
||||
|
||||
@@ -422,6 +423,30 @@ export const focusChunkAt: {
|
||||
(a, b) => Effect.succeed(Chunk.replace(a, index, b))),
|
||||
)
|
||||
|
||||
/**
|
||||
* Narrows the focus to the value inside an `Option`.
|
||||
*
|
||||
* Reading or writing through this lens fails with `NoSuchElementException` when the parent option is `None`.
|
||||
* Writing wraps the new focused value back into `Option.some`.
|
||||
*/
|
||||
export const focusOption: {
|
||||
<A, ER, EW, RR, RW>(
|
||||
self: Lens<Option.Option<A>, ER, EW, RR, RW>,
|
||||
): Lens<A, ER | NoSuchElementException, EW | NoSuchElementException, RR, RW>
|
||||
} = <A, ER, EW, RR, RW>(
|
||||
self: Lens<Option.Option<A>, ER, EW, RR, RW>,
|
||||
): Lens<A, ER | NoSuchElementException, EW | NoSuchElementException, RR, RW> => mapEffect(
|
||||
self,
|
||||
option => Option.match(option, {
|
||||
onSome: value => Effect.succeed(value),
|
||||
onNone: () => Effect.fail(new Cause.NoSuchElementException()),
|
||||
}),
|
||||
(option, value) => Option.match(option, {
|
||||
onSome: () => Effect.succeed(Option.some(value)),
|
||||
onNone: () => Effect.fail(new Cause.NoSuchElementException()),
|
||||
}),
|
||||
)
|
||||
|
||||
|
||||
/**
|
||||
* Reads the current value from a `Lens`.
|
||||
|
||||
Reference in New Issue
Block a user