0.2.0 #5
@@ -1,4 +1,4 @@
|
|||||||
import { Array, Chunk, type Context, Effect, Function, identity, type ManagedRuntime, Option, Pipeable, Predicate, Readable, type Runtime, Stream, type SubscriptionRef, type SynchronizedRef } from "effect"
|
import { Array, Chunk, type Context, Effect, Function, identity, type ManagedRuntime, Option, Pipeable, Predicate, PubSub, Readable, Ref, type Runtime, 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"
|
||||||
|
|
||||||
@@ -17,80 +17,167 @@ export interface Lens<in out A, in out ER = never, in out EW = never, in out RR
|
|||||||
extends Subscribable.Subscribable<A, ER, RR> {
|
extends Subscribable.Subscribable<A, ER, RR> {
|
||||||
readonly [LensTypeId]: LensTypeId
|
readonly [LensTypeId]: LensTypeId
|
||||||
|
|
||||||
readonly modify: <B, E1 = never, R1 = never>(
|
readonly modifyEffect: <B, E1 = never, R1 = never>(
|
||||||
f: (a: A) => Effect.Effect<readonly [B, A], E1, R1>
|
f: (a: A) => Effect.Effect<readonly [B, A], E1, R1>
|
||||||
) => Effect.Effect<B, ER | EW | E1, RR | RW | R1>
|
) => Effect.Effect<B, ER | EW | E1, RR | RW | R1>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal `Lens` implementation.
|
|
||||||
*/
|
|
||||||
export 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 [Readable.TypeId]: Readable.TypeId = Readable.TypeId
|
|
||||||
readonly [Subscribable.TypeId]: Subscribable.TypeId = Subscribable.TypeId
|
|
||||||
readonly [LensTypeId]: LensTypeId = LensTypeId
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
readonly get: Effect.Effect<A, ER, RR>,
|
|
||||||
readonly changes: Stream.Stream<A, ER, RR>,
|
|
||||||
readonly modify: <B, E1 = never, R1 = never>(
|
|
||||||
f: (a: A) => Effect.Effect<readonly [B, A], E1, R1>
|
|
||||||
) => Effect.Effect<B, ER | EW | E1, RR | RW | R1>,
|
|
||||||
) {
|
|
||||||
super()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether a value is a `Lens`.
|
* Checks whether a value is a `Lens`.
|
||||||
*/
|
*/
|
||||||
export const isLens = (u: unknown): u is Lens<unknown, unknown, unknown, unknown, unknown> => Predicate.hasProperty(u, LensTypeId)
|
export const isLens = (u: unknown): u is Lens<unknown, unknown, unknown, unknown, unknown> => Predicate.hasProperty(u, LensTypeId)
|
||||||
|
|
||||||
|
export const LensWithInternalsTypeId: unique symbol = Symbol.for("@effect-fc/Lens/LensWithInternals")
|
||||||
|
export type LensWithInternalsTypeId = typeof LensWithInternalsTypeId
|
||||||
|
|
||||||
|
export interface LensWithInternals<in out A, in out ER = never, in out EW = never, in out RR = never, in out RW = never>
|
||||||
|
extends Lens<A, ER, EW, RR, RW> {
|
||||||
|
readonly [LensWithInternalsTypeId]: LensWithInternalsTypeId
|
||||||
|
|
||||||
|
readonly update: (a: A) => Effect.Effect<void, EW, RW>
|
||||||
|
readonly semaphore: Effect.Semaphore
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isLensWithInternals = (u: unknown): u is LensWithInternals<unknown, unknown, unknown, unknown, unknown> => Predicate.hasProperty(u, LensWithInternalsTypeId)
|
||||||
|
|
||||||
|
|
||||||
|
export declare namespace LensImpl {
|
||||||
|
export interface Source<in out A, in out ER = never, in out EW = never, in out RR = never, in out RW = never> {
|
||||||
|
readonly get: Effect.Effect<A, ER, RR>,
|
||||||
|
readonly changes: Stream.Stream<A, ER, RR>,
|
||||||
|
readonly update: (a: A) => Effect.Effect<void, EW, RW>,
|
||||||
|
readonly semaphore: Effect.Semaphore,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export 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 LensWithInternals<A, ER, EW, RR, RW> {
|
||||||
|
readonly [Readable.TypeId]: Readable.TypeId = Readable.TypeId
|
||||||
|
readonly [Subscribable.TypeId]: Subscribable.TypeId = Subscribable.TypeId
|
||||||
|
readonly [LensTypeId]: LensTypeId = LensTypeId
|
||||||
|
readonly [LensWithInternalsTypeId]: LensWithInternalsTypeId = LensWithInternalsTypeId
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
readonly get: Effect.Effect<A, ER, RR>,
|
||||||
|
readonly changes: Stream.Stream<A, ER, RR>,
|
||||||
|
readonly update: (a: A) => Effect.Effect<void, EW, RW>,
|
||||||
|
readonly semaphore: Effect.Semaphore,
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
modifyEffect<B, E1 = never, R1 = never>(
|
||||||
|
f: (a: A) => Effect.Effect<readonly [B, A], E1, R1>
|
||||||
|
) {
|
||||||
|
return this.semaphore.withPermits(1)(
|
||||||
|
Effect.flatMap(
|
||||||
|
this.get,
|
||||||
|
a => Effect.flatMap(f(a), ([b, next]) => Effect.as(this.update(next), b),
|
||||||
|
))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a `Lens` by supplying how to read the current value, observe changes, and apply transformations.
|
* Creates a `Lens` by supplying how to read the current value, observe changes, and apply transformations.
|
||||||
*
|
|
||||||
* Either `modify` or `set` needs to be supplied.
|
|
||||||
*/
|
*/
|
||||||
export const make = <A, ER, EW, RR, RW>(
|
export const make = <A, ER, EW, RR, RW>(
|
||||||
options: {
|
source: LensImpl.Source<A, ER, EW, RR, RW>
|
||||||
readonly get: Effect.Effect<A, ER, RR>
|
): Lens<A, ER, EW, RR, RW> => new LensImpl(source.get, source.changes, source.update, source.semaphore)
|
||||||
readonly changes: Stream.Stream<A, ER, RR>
|
|
||||||
} & (
|
|
||||||
| {
|
export class LensLazyImpl<in out A, in out ER = never, in out EW = never, in out RR = never, in out RW = never>
|
||||||
readonly modify: <B, E1 = never, R1 = never>(
|
extends Pipeable.Class() implements LensWithInternals<A, ER, EW, RR, RW> {
|
||||||
f: (a: A) => Effect.Effect<readonly [B, A], E1, R1>
|
readonly [Readable.TypeId]: Readable.TypeId = Readable.TypeId
|
||||||
) => Effect.Effect<B, ER | EW | E1, RR | RW | R1>
|
readonly [Subscribable.TypeId]: Subscribable.TypeId = Subscribable.TypeId
|
||||||
|
readonly [LensTypeId]: LensTypeId = LensTypeId
|
||||||
|
readonly [LensWithInternalsTypeId]: LensWithInternalsTypeId = LensWithInternalsTypeId
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
readonly source: LensImpl.Source<A, ER, EW, RR, RW>
|
||||||
|
) {
|
||||||
|
super()
|
||||||
}
|
}
|
||||||
| { readonly set: (a: A) => Effect.Effect<void, EW, RW> }
|
|
||||||
)
|
get get() { return this.source.get }
|
||||||
): Lens<A, ER, EW, RR, RW> => new LensImpl<A, ER, EW, RR, RW>(
|
get changes() { return this.source.changes }
|
||||||
options.get,
|
get update() { return this.source.update }
|
||||||
options.changes,
|
get semaphore() { return this.source.semaphore }
|
||||||
Predicate.hasProperty(options, "modify")
|
|
||||||
? options.modify
|
modifyEffect<B, E1 = never, R1 = never>(
|
||||||
: <B, E1 = never, R1 = never>(
|
|
||||||
f: (a: A) => Effect.Effect<readonly [B, A], E1, R1>
|
f: (a: A) => Effect.Effect<readonly [B, A], E1, R1>
|
||||||
) => Effect.flatMap(
|
) {
|
||||||
options.get,
|
return this.semaphore.withPermits(1)(
|
||||||
a => Effect.flatMap(f(a), ([b, next]) => Effect.as(options.set(next), b)
|
Effect.flatMap(
|
||||||
)),
|
this.get,
|
||||||
|
a => Effect.flatMap(f(a), ([b, next]) => Effect.as(this.update(next), b),
|
||||||
|
))
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a `Lens` by supplying how to read the current value, observe changes, and apply transformations.
|
||||||
|
*/
|
||||||
|
export const makeLazy = <A, ER, EW, RR, RW>(
|
||||||
|
source: LensImpl.Source<A, ER, EW, RR, RW>
|
||||||
|
): Lens<A, ER, EW, RR, RW> => new LensLazyImpl(source)
|
||||||
|
|
||||||
|
|
||||||
|
export declare namespace SubscriptionRefLensImpl {
|
||||||
|
export interface SubscriptionRefWithInternals<in out A>
|
||||||
|
extends SubscriptionRef.SubscriptionRef<A> {
|
||||||
|
readonly ref: Ref.Ref<A>
|
||||||
|
readonly pubsub: PubSub.PubSub<A>
|
||||||
|
readonly semaphore: Effect.Semaphore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SubscriptionRefLensImpl<in out A>
|
||||||
|
extends Pipeable.Class() implements LensWithInternals<A, never, never, never, never> {
|
||||||
|
readonly [Readable.TypeId]: Readable.TypeId = Readable.TypeId
|
||||||
|
readonly [Subscribable.TypeId]: Subscribable.TypeId = Subscribable.TypeId
|
||||||
|
readonly [LensTypeId]: LensTypeId = LensTypeId
|
||||||
|
readonly [LensWithInternalsTypeId]: LensWithInternalsTypeId = LensWithInternalsTypeId
|
||||||
|
|
||||||
|
readonly ref: SubscriptionRefLensImpl.SubscriptionRefWithInternals<A>
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
ref: SubscriptionRef.SubscriptionRef<A>
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
this.ref = ref as SubscriptionRefLensImpl.SubscriptionRefWithInternals<A>
|
||||||
|
}
|
||||||
|
|
||||||
|
get get() { return this.ref.get }
|
||||||
|
get changes() { return this.ref.changes }
|
||||||
|
update(a: A) {
|
||||||
|
return Effect.zipLeft(
|
||||||
|
Ref.set(this.ref.ref, a),
|
||||||
|
PubSub.publish(this.ref.pubsub, a),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
get semaphore() { return this.ref.semaphore }
|
||||||
|
|
||||||
|
modifyEffect<B, E1 = never, R1 = never>(
|
||||||
|
f: (a: A) => Effect.Effect<readonly [B, A], E1, R1>
|
||||||
|
) {
|
||||||
|
return this.semaphore.withPermits(1)(
|
||||||
|
Effect.flatMap(
|
||||||
|
this.get,
|
||||||
|
a => Effect.flatMap(f(a), ([b, next]) => Effect.as(this.update(next), b),
|
||||||
|
))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a `Lens` that proxies a `SubscriptionRef`.
|
* Creates a `Lens` that proxies a `SubscriptionRef`.
|
||||||
*/
|
*/
|
||||||
export const fromSubscriptionRef = <A>(
|
export const fromSubscriptionRef = <A>(
|
||||||
ref: SubscriptionRef.SubscriptionRef<A>
|
ref: SubscriptionRef.SubscriptionRef<A>
|
||||||
): Lens<A, never, never, never, never> => make({
|
): Lens<A, never, never, never, never> => new SubscriptionRefLensImpl(ref)
|
||||||
get get() { return ref.get },
|
|
||||||
get changes() { return ref.changes },
|
|
||||||
modify: <B, E1 = never, R1 = never>(
|
|
||||||
f: (a: A) => Effect.Effect<readonly [B, A], E1, R1>
|
|
||||||
) => ref.modifyEffect(f),
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a `Lens` that proxies a `SynchronizedRef`.
|
* Creates a `Lens` that proxies a `SynchronizedRef`.
|
||||||
@@ -108,6 +195,7 @@ export const fromSynchronizedRef = <A>(
|
|||||||
) => ref.modifyEffect(f),
|
) => ref.modifyEffect(f),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flattens an effectful `Lens`.
|
* Flattens an effectful `Lens`.
|
||||||
*/
|
*/
|
||||||
@@ -116,11 +204,9 @@ export const unwrap = <A, ER, EW, RR, RW, E1, R1>(
|
|||||||
): Lens<A, ER | E1, EW | E1, RR | R1, RW | R1> => make({
|
): Lens<A, ER | E1, EW | E1, RR | R1, RW | R1> => make({
|
||||||
get: Effect.flatMap(effect, l => l.get),
|
get: Effect.flatMap(effect, l => l.get),
|
||||||
changes: Stream.unwrap(Effect.map(effect, l => l.changes)),
|
changes: Stream.unwrap(Effect.map(effect, l => l.changes)),
|
||||||
modify: <B, E2 = never, R2 = never>(
|
update: a => Effect.flatMap(effect, l => (l as LensWithInternals<A, ER, EW, RR, RW>).update(a))
|
||||||
f: (a: A) => Effect.Effect<readonly [B, A], E2, R2>
|
|
||||||
) => Effect.flatMap(effect, l => l.modify(f)),
|
|
||||||
})
|
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Derives a new `Lens` by applying synchronous getters and setters over the focused value.
|
* Derives a new `Lens` by applying synchronous getters and setters over the focused value.
|
||||||
|
|||||||
Reference in New Issue
Block a user