@@ -0,0 +1,137 @@
|
||||
# Effect Lens Next
|
||||
|
||||
An effectful Lens for Effect v4.
|
||||
|
||||
`effect-lens-next` provides a bidirectional view into shared state. A Lens can read, observe, and atomically update a value while focusing on a nested part of a larger structure.
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
npm install effect@4.0.0-beta.85 effect-lens-next
|
||||
```
|
||||
|
||||
This release targets Effect `4.0.0-beta.85` exactly while Effect v4 is in beta.
|
||||
|
||||
## Quickstart
|
||||
|
||||
```ts
|
||||
import { Effect, Stream, SubscriptionRef } from "effect"
|
||||
import { Lens } from "effect-lens-next"
|
||||
|
||||
const program = Effect.gen(function*() {
|
||||
const state = yield* SubscriptionRef.make({ count: 0, label: "Counter" })
|
||||
|
||||
const count = state.pipe(
|
||||
Lens.fromSubscriptionRef,
|
||||
Lens.focusObjectOn("count")
|
||||
)
|
||||
|
||||
yield* Lens.update(count, (value) => value + 1)
|
||||
const current = yield* Lens.get(count)
|
||||
|
||||
yield* count.changes.pipe(
|
||||
Stream.take(1),
|
||||
Stream.runForEach((value) => Effect.log(`count: ${value}`))
|
||||
)
|
||||
|
||||
return current
|
||||
})
|
||||
```
|
||||
|
||||
## Lens Type
|
||||
|
||||
```ts
|
||||
Lens.Lens<
|
||||
A, // focused value
|
||||
ER, // read errors
|
||||
EW, // write errors
|
||||
RR, // read requirements
|
||||
RW // write requirements
|
||||
>
|
||||
```
|
||||
|
||||
## Constructors
|
||||
|
||||
- `Lens.fromSubscriptionRef` creates a reactive Lens.
|
||||
- `Lens.fromSynchronizedRef` creates a non-reactive Lens whose stream emits its value when evaluated.
|
||||
- `Lens.fromRef` creates a non-reactive Lens and returns it in an Effect because it allocates a semaphore.
|
||||
- `Lens.make` creates a Lens from custom `get`, `changes`, `commit`, and `lock` operations.
|
||||
- `Lens.unwrap` flattens an effect that produces a Lens.
|
||||
|
||||
## Focusing
|
||||
|
||||
```ts
|
||||
import { Effect, SubscriptionRef } from "effect"
|
||||
import { Lens } from "effect-lens-next"
|
||||
|
||||
const program = Effect.gen(function*() {
|
||||
const state = yield* SubscriptionRef.make({
|
||||
users: [{ name: "Ada", score: 10 }]
|
||||
})
|
||||
|
||||
const score = state.pipe(
|
||||
Lens.fromSubscriptionRef,
|
||||
Lens.focusObjectOn("users"),
|
||||
Lens.focusArrayAt(0),
|
||||
Lens.focusObjectOn("score")
|
||||
)
|
||||
|
||||
yield* Lens.set(score, 11)
|
||||
})
|
||||
```
|
||||
|
||||
Available focus operations include object fields, mutable object fields, arrays, mutable arrays, tuples, mutable tuples, `Chunk` values, and `Option` values. Safe indexed operations fail with `Cause.NoSuchElementError` when the index is absent.
|
||||
|
||||
Effect v4 no longer treats `Option` as an Effect. Custom effectful mappings should convert optional values explicitly:
|
||||
|
||||
```ts
|
||||
import { Array, Effect } from "effect"
|
||||
|
||||
Lens.mapEffect(
|
||||
(values: ReadonlyArray<number>) => Effect.fromOption(Array.get(values, 0)),
|
||||
(values, value) => Effect.fromOption(Array.replace(values, 0, value))
|
||||
)
|
||||
```
|
||||
|
||||
## Updating
|
||||
|
||||
The update API follows Effect v4's `SubscriptionRef` conventions:
|
||||
|
||||
- `get`, `set`, `getAndSet`, `setAndGet`
|
||||
- `modify`, `modifyEffect`
|
||||
- `update`, `updateEffect`
|
||||
- `getAndUpdate`, `getAndUpdateEffect`
|
||||
- `updateAndGet`, `updateAndGetEffect`
|
||||
- `modifySome`, `modifySomeEffect`
|
||||
- `getAndUpdateSome`, `getAndUpdateSomeEffect`
|
||||
- `updateSome`, `updateSomeEffect`
|
||||
- `updateSomeAndGet`, `updateSomeAndGetEffect`
|
||||
|
||||
Conditional updates use `Option.none()` to skip both the write and change notification:
|
||||
|
||||
```ts
|
||||
import { Effect, Option } from "effect"
|
||||
|
||||
yield* Lens.updateSome(lens, (value) =>
|
||||
value < 10 ? Option.some(value + 1) : Option.none()
|
||||
)
|
||||
|
||||
yield* Lens.updateSomeEffect(lens, (value) =>
|
||||
Effect.succeed(value < 10 ? Option.some(value + 1) : Option.none())
|
||||
)
|
||||
```
|
||||
|
||||
## Subscribable
|
||||
|
||||
Effect v4 removed its `Readable` and `Subscribable` modules. This package provides a small compatible abstraction with `get` and `changes` properties:
|
||||
|
||||
```ts
|
||||
import { Subscribable } from "effect-lens-next"
|
||||
|
||||
const users = Subscribable.fromSubscriptionRef(usersRef)
|
||||
const count = Subscribable.focusArrayLength(users)
|
||||
|
||||
const current = yield* count.get
|
||||
```
|
||||
|
||||
The module includes `make`, `map`, `mapEffect`, `unwrap`, `fromSubscriptionRef`, and the same readonly focus helpers used by Lens.
|
||||
Reference in New Issue
Block a user