0.2.1 + 2.0.0-beta.0 #6

Merged
Thilawyn merged 20 commits from next into master 2026-06-22 01:35:08 +02:00
4 changed files with 53 additions and 34 deletions
Showing only changes of commit 364536b35a - Show all commits
+3 -3
View File
@@ -126,12 +126,12 @@ yield* Lens.updateSomeEffect(lens, (value) =>
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"
import { Lens, Subscribable } from "effect-lens-next"
const users = Subscribable.fromSubscriptionRef(usersRef)
const users = Lens.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.
The module includes `make`, `map`, `mapEffect`, `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.
+1 -1
View File
@@ -66,7 +66,7 @@ export declare namespace LensImpl {
export abstract class LensImpl<in out A, in out ER = never, in out EW = never, in out RR = never, in out RW = never>
extends Pipeable.Class implements Lens<A, ER, EW, RR, RW> {
readonly [Subscribable.TypeId]: Subscribable.TypeId = Subscribable.TypeId
readonly [Subscribable.SubscribableTypeId]: Subscribable.SubscribableTypeId = Subscribable.SubscribableTypeId
readonly [LensTypeId]: LensTypeId = LensTypeId
readonly [LensImplTypeId]: LensImplTypeId = LensImplTypeId
@@ -1,5 +1,6 @@
import { describe, expect, test } from "bun:test"
import { Chunk, Effect, SubscriptionRef } from "effect"
import * as Lens from "./Lens.js"
import * as Subscribable from "./Subscribable.js"
@@ -9,7 +10,7 @@ describe("Subscribable", () => {
Effect.flatMap(
SubscriptionRef.make([1, 2, 3]),
parent => {
const sizeSub = Subscribable.focusArrayLength(Subscribable.fromSubscriptionRef(parent))
const sizeSub = Subscribable.focusArrayLength(Lens.fromSubscriptionRef(parent))
return Effect.flatMap(
sizeSub.get,
initial => Effect.flatMap(
@@ -29,7 +30,7 @@ describe("Subscribable", () => {
Effect.flatMap(
SubscriptionRef.make(Chunk.make(1, 2) as Chunk.Chunk<number>),
parent => {
const sizeSub = Subscribable.focusChunkSize(Subscribable.fromSubscriptionRef(parent))
const sizeSub = Subscribable.focusChunkSize(Lens.fromSubscriptionRef(parent))
return Effect.flatMap(
sizeSub.get,
initial => Effect.flatMap(
@@ -49,7 +50,7 @@ describe("Subscribable", () => {
Effect.flatMap(
SubscriptionRef.make([1, 2, 3]),
parent => {
const sizeSub = Subscribable.focusIterableSize(Subscribable.fromSubscriptionRef(parent))
const sizeSub = Subscribable.focusIterableSize(Lens.fromSubscriptionRef(parent))
return Effect.flatMap(
sizeSub.get,
initial => Effect.flatMap(
+45 -27
View File
@@ -9,43 +9,68 @@ import {
Pipeable,
Predicate,
Stream,
SubscriptionRef,
} from "effect"
export const TypeId: unique symbol = Symbol.for("@effect-fc/Lens/v4/Subscribable")
export type TypeId = typeof TypeId
export const SubscribableTypeId: unique symbol = Symbol.for("@effect-fc/Lens/v4/Subscribable")
export type SubscribableTypeId = typeof SubscribableTypeId
export interface Subscribable<in out A, in out E = never, in out R = never> extends Pipeable.Pipeable {
readonly [TypeId]: TypeId
readonly [SubscribableTypeId]: SubscribableTypeId
readonly get: Effect.Effect<A, E, R>
readonly changes: Stream.Stream<A, E, R>
}
class SubscribableImpl<in out A, in out E, in out R>
extends Pipeable.Class implements Subscribable<A, E, R> {
readonly [TypeId]: TypeId = TypeId
export const isSubscribable = (u: unknown): u is Subscribable<unknown, unknown, unknown> => Predicate.hasProperty(u, SubscribableTypeId)
constructor(
readonly get: Effect.Effect<A, E, R>,
readonly changes: Stream.Stream<A, E, R>,
) {
super()
export const SubscribableImplTypeId: unique symbol = Symbol.for("@effect-fc/Lens/v4/SubscribableImpl")
export type SubscribableImplTypeId = typeof SubscribableImplTypeId
export declare namespace SubscribableImpl {
export interface Source<in out A, in out E = never, in out R = never> {
readonly get: Effect.Effect<A, E, R>
readonly changes: Stream.Stream<A, E, R>
}
}
export const isSubscribable = (u: unknown): u is Subscribable<unknown, unknown, unknown> => Predicate.hasProperty(u, TypeId)
export class SubscribableImpl<in out A, in out E = never, in out R = never>
extends Pipeable.Class implements Subscribable<A, E, R> {
readonly [SubscribableTypeId]: SubscribableTypeId = SubscribableTypeId
readonly [SubscribableImplTypeId]: SubscribableImplTypeId = SubscribableImplTypeId
export const make = <A, E, R>(options: {
readonly get: Effect.Effect<A, E, R>
readonly changes: Stream.Stream<A, E, R>
}): Subscribable<A, E, R> => new SubscribableImpl(options.get, options.changes)
constructor(
readonly source: SubscribableImpl.Source<A, E, R>,
) {
super()
}
export const fromSubscriptionRef = <A>(self: SubscriptionRef.SubscriptionRef<A>): Subscribable<A> => make({
get: SubscriptionRef.get(self),
changes: SubscriptionRef.changes(self),
get get() { return this.source.get }
get changes() { return this.source.changes }
}
export const isSubscribableImpl = (u: unknown): u is SubscribableImpl<unknown, unknown, unknown> => Predicate.hasProperty(u, SubscribableImplTypeId)
export const asSubscribableImpl = <A, E, R>(
subscribable: Subscribable<A, E, R>
): SubscribableImpl<A, E, R> => {
if (!isSubscribableImpl(subscribable))
throw new Error("Not a 'SubscribableImpl'")
return subscribable as SubscribableImpl<A, E, R>
}
export const make = <A, E, R>(
source: SubscribableImpl.Source<A, E, R>
): Subscribable<A, E, R> => new SubscribableImpl(source)
export const unwrap = <A, E, R, E1, R1>(
effect: Effect.Effect<Subscribable<A, E, R>, E1, R1>,
): Subscribable<A, E | E1, R | R1> => make({
get: Effect.flatMap(effect, self => self.get),
changes: Stream.unwrap(Effect.map(effect, self => self.changes)),
})
export const map: {
<A, B>(f: (a: NoInfer<A>) => B): <E, R>(self: Subscribable<A, E, R>) => Subscribable<B, E, R>
<A, E, R, B>(self: Subscribable<A, E, R>, f: (a: NoInfer<A>) => B): Subscribable<B, E, R>
@@ -65,13 +90,6 @@ export const mapEffect: {
changes: Stream.mapEffect(self.changes, f),
}))
export const unwrap = <A, E, R, E1, R1>(
effect: Effect.Effect<Subscribable<A, E, R>, E1, R1>,
): Subscribable<A, E | E1, R | R1> => make({
get: Effect.flatMap(effect, self => self.get),
changes: Stream.unwrap(Effect.map(effect, self => self.changes)),
})
/** Maps over an `Option` value in the `Subscribable`. */
export const mapOption: {
<A, B>(f: (a: A) => B): <E, R>(self: Subscribable<Option.Option<A>, E, R>) => Subscribable<Option.Option<B>, E, R>