Files
effect-lens/packages/effect-lens-next/README.md
T
Julien Valverdé e15802976d
Lint / lint (push) Successful in 16s
Add additional error handling primitives for Subscribable
2026-06-21 23:32:29 +02:00

138 lines
3.9 KiB
Markdown

# 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 { Lens, Subscribable } from "effect-lens-next"
const users = Lens.fromSubscriptionRef(usersRef)
const count = Subscribable.focusArrayLength(users)
const current = yield* count.get
```
The module includes value transforms, error recovery (`catch`, `catchCause`, `orElse`, `orElseSucceed`, and `retry`), error inspection, `unwrap`, and the same readonly focus helpers used by Lens. A `SubscriptionRef` can first be converted with `Lens.fromSubscriptionRef`, since every Lens is already a Subscribable.