210 lines
9.1 KiB
TypeScript
210 lines
9.1 KiB
TypeScript
import { Chunk, Effect, Function, Pipeable, Predicate, Readable, Stream, Subscribable, SubscriptionRef } from "effect"
|
|
|
|
|
|
export const LensTypeId: unique symbol = Symbol.for("@effect-fc/Lens/Lens")
|
|
export type LensTypeId = typeof LensTypeId
|
|
|
|
export interface Lens<in out A, in out ER = never, in out EW = never, in out RR = never, in out RW = never>
|
|
extends Subscribable.Subscribable<A, ER, RR> {
|
|
readonly [LensTypeId]: LensTypeId
|
|
|
|
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>
|
|
}
|
|
|
|
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()
|
|
}
|
|
}
|
|
|
|
|
|
export const isLens = (u: unknown): u is Lens<unknown, unknown, unknown, unknown, unknown> => Predicate.hasProperty(u, LensTypeId)
|
|
|
|
|
|
export const make = <A, ER, EW, RR, RW>(
|
|
options: {
|
|
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>
|
|
}
|
|
| { readonly set: (a: A) => Effect.Effect<void, EW, RW> }
|
|
)
|
|
): Lens<A, ER, EW, RR, RW> => new LensImpl<A, ER, EW, RR, RW>(
|
|
options.get,
|
|
options.changes,
|
|
Predicate.hasProperty(options, "modify")
|
|
? options.modify
|
|
: <B, E1 = never, R1 = never>(
|
|
f: (a: A) => Effect.Effect<readonly [B, A], E1, R1>
|
|
) => Effect.flatMap(
|
|
options.get,
|
|
a => Effect.flatMap(f(a), ([b, next]) => Effect.as(options.set(next), b)
|
|
)),
|
|
)
|
|
|
|
/**
|
|
* Creates a `Lens` that directly proxies a `SubscriptionRef`.
|
|
*/
|
|
export const fromSubscriptionRef = <A>(
|
|
ref: SubscriptionRef.SubscriptionRef<A>
|
|
): Lens<A, never, never, never, never> => make({
|
|
get get() { return ref.get },
|
|
get changes() { return ref.changes },
|
|
modify: ref.modifyEffect.bind(ref), // TODO
|
|
})
|
|
|
|
export const unwrap = <A, ER, EW, RR, RW, E1, R1>(
|
|
effect: Effect.Effect<Lens<A, ER, EW, RR, RW>, E1, R1>
|
|
): Lens<A, ER | E1, EW | E1, RR | R1, RW | R1> => make({
|
|
get: Effect.flatMap(effect, l => l.get),
|
|
changes: Stream.unwrap(Effect.map(effect, l => l.changes)),
|
|
modify: <B, E2 = never, R2 = never>(
|
|
f: (a: A) => Effect.Effect<readonly [B, A], E2, R2>
|
|
) => Effect.flatMap(effect, l => l.modify(f)),
|
|
})
|
|
|
|
|
|
export const map = <A, ER, EW, RR, RW, B>(
|
|
self: Lens<A, ER, EW, RR, RW>,
|
|
get: (a: NoInfer<A>) => B,
|
|
set: (a: NoInfer<A>, b: B) => NoInfer<A>,
|
|
): Lens<B, ER, EW, RR, RW> => make({
|
|
get get() { return Effect.map(self.get, get) },
|
|
get changes() { return Stream.map(self.changes, get) },
|
|
modify: <C, E1 = never, R1 = never>(
|
|
f: (b: B) => Effect.Effect<readonly [C, B], E1, R1>
|
|
) => self.modify(a =>
|
|
Effect.flatMap(f(get(a)), ([c, next]) => Effect.succeed([c, set(a, next)]))
|
|
),
|
|
})
|
|
|
|
export const mapEffect = <A, ER, EW, RR, RW, B, EGet = never, RGet = never, ESet = never, RSet = never>(
|
|
self: Lens<A, ER, EW, RR, RW>,
|
|
get: (a: NoInfer<A>) => Effect.Effect<B, EGet, RGet>,
|
|
set: (a: NoInfer<A>, b: B) => Effect.Effect<NoInfer<A>, ESet, RSet>,
|
|
): Lens<B, ER | EGet, EW | ESet, RR | RGet, RW | RSet> => make({
|
|
get get() { return Effect.flatMap(self.get, get) },
|
|
get changes() { return Stream.mapEffect(self.changes, get) },
|
|
modify: <C, E1 = never, R1 = never>(
|
|
f: (b: B) => Effect.Effect<readonly [C, B], E1, R1>
|
|
) => self.modify(a => Effect.flatMap(
|
|
get(a),
|
|
b => Effect.flatMap(
|
|
f(b),
|
|
([c, bNext]) => Effect.flatMap(
|
|
set(a, bNext),
|
|
nextA => Effect.succeed([c, nextA] as const),
|
|
),
|
|
)
|
|
)),
|
|
})
|
|
|
|
|
|
export const get = <A, ER, EW, RR, RW>(self: Lens<A, ER, EW, RR, RW>): Effect.Effect<A, ER, RR> => self.get
|
|
|
|
export const set: {
|
|
<A, ER, EW, RR, RW>(value: A): (self: Lens<A, ER, EW, RR, RW>) => Effect.Effect<void, ER | EW, RR | RW>
|
|
<A, ER, EW, RR, RW>(self: Lens<A, ER, EW, RR, RW>, value: A): Effect.Effect<void, ER | EW, RR | RW>
|
|
} = Function.dual(2, <A, ER, EW, RR, RW>(self: Lens<A, ER, EW, RR, RW>, value: A) =>
|
|
self.modify<void, never, never>(() => Effect.succeed([void 0, value] as const)),
|
|
)
|
|
|
|
export const getAndSet: {
|
|
<A, ER, EW, RR, RW>(value: A): (self: Lens<A, ER, EW, RR, RW>) => Effect.Effect<A, ER | EW, RR | RW>
|
|
<A, ER, EW, RR, RW>(self: Lens<A, ER, EW, RR, RW>, value: A): Effect.Effect<A, ER | EW, RR | RW>
|
|
} = Function.dual(2, <A, ER, EW, RR, RW>(self: Lens<A, ER, EW, RR, RW>, value: A) =>
|
|
self.modify<A, never, never>(a => Effect.succeed([a, value] as const)),
|
|
)
|
|
|
|
export const update: {
|
|
<A, ER, EW, RR, RW>(f: (a: A) => A): (self: Lens<A, ER, EW, RR, RW>) => Effect.Effect<void, ER | EW, RR | RW>
|
|
<A, ER, EW, RR, RW>(self: Lens<A, ER, EW, RR, RW>, f: (a: A) => A): Effect.Effect<void, ER | EW, RR | RW>
|
|
} = Function.dual(2, <A, ER, EW, RR, RW>(self: Lens<A, ER, EW, RR, RW>, f: (a: A) => A) =>
|
|
self.modify<void, never, never>(a => Effect.succeed([void 0, f(a)] as const)),
|
|
)
|
|
|
|
export const updateEffect: {
|
|
<A, ER, EW, RR, RW, E, R>(f: (a: A) => Effect.Effect<A, E, R>): (self: Lens<A, ER, EW, RR, RW>) => Effect.Effect<void, ER | EW | E, RR | RW | R>
|
|
<A, ER, EW, RR, RW, E, R>(self: Lens<A, ER, EW, RR, RW>, f: (a: A) => Effect.Effect<A, E, R>): Effect.Effect<void, ER | EW | E, RR | RW | R>
|
|
} = Function.dual(2, <A, ER, EW, RR, RW, E, R>(self: Lens<A, ER, EW, RR, RW>, f: (a: A) => Effect.Effect<A, E, R>) =>
|
|
self.modify<void, E, R>(a => Effect.flatMap(
|
|
f(a),
|
|
next => Effect.succeed([void 0, next] as const),
|
|
)),
|
|
)
|
|
|
|
export const getAndUpdate: {
|
|
<A, ER, EW, RR, RW>(f: (a: A) => A): (self: Lens<A, ER, EW, RR, RW>) => Effect.Effect<A, ER | EW, RR | RW>
|
|
<A, ER, EW, RR, RW>(self: Lens<A, ER, EW, RR, RW>, f: (a: A) => A): Effect.Effect<A, ER | EW, RR | RW>
|
|
} = Function.dual(2, <A, ER, EW, RR, RW>(self: Lens<A, ER, EW, RR, RW>, f: (a: A) => A) =>
|
|
self.modify<A, never, never>(a => Effect.succeed([a, f(a)] as const)),
|
|
)
|
|
|
|
export const getAndUpdateEffect: {
|
|
<A, ER, EW, RR, RW, E, R>(f: (a: A) => Effect.Effect<A, E, R>): (self: Lens<A, ER, EW, RR, RW>) => Effect.Effect<A, ER | EW | E, RR | RW | R>
|
|
<A, ER, EW, RR, RW, E, R>(self: Lens<A, ER, EW, RR, RW>, f: (a: A) => Effect.Effect<A, E, R>): Effect.Effect<A, ER | EW | E, RR | RW | R>
|
|
} = Function.dual(2, <A, ER, EW, RR, RW, E, R>(self: Lens<A, ER, EW, RR, RW>, f: (a: A) => Effect.Effect<A, E, R>) =>
|
|
self.modify<A, E, R>(a => Effect.flatMap(
|
|
f(a),
|
|
next => Effect.succeed([a, next] as const)
|
|
)),
|
|
)
|
|
|
|
export const setAndGet: {
|
|
<A, ER, EW, RR, RW>(value: A): (self: Lens<A, ER, EW, RR, RW>) => Effect.Effect<A, ER | EW, RR | RW>
|
|
<A, ER, EW, RR, RW>(self: Lens<A, ER, EW, RR, RW>, value: A): Effect.Effect<A, ER | EW, RR | RW>
|
|
} = Function.dual(2, <A, ER, EW, RR, RW>(self: Lens<A, ER, EW, RR, RW>, value: A) =>
|
|
self.modify<A, never, never>(() => Effect.succeed([value, value] as const)),
|
|
)
|
|
|
|
export const updateAndGet: {
|
|
<A, ER, EW, RR, RW>(f: (a: A) => A): (self: Lens<A, ER, EW, RR, RW>) => Effect.Effect<A, ER | EW, RR | RW>
|
|
<A, ER, EW, RR, RW>(self: Lens<A, ER, EW, RR, RW>, f: (a: A) => A): Effect.Effect<A, ER | EW, RR | RW>
|
|
} = Function.dual(2, <A, ER, EW, RR, RW>(self: Lens<A, ER, EW, RR, RW>, f: (a: A) => A) =>
|
|
self.modify<A, never, never>(a => {
|
|
const next = f(a)
|
|
return Effect.succeed([next, next] as const)
|
|
}),
|
|
)
|
|
|
|
export const updateAndGetEffect: {
|
|
<A, ER, EW, RR, RW, E, R>(f: (a: A) => Effect.Effect<A, E, R>): (self: Lens<A, ER, EW, RR, RW>) => Effect.Effect<A, ER | EW | E, RR | RW | R>
|
|
<A, ER, EW, RR, RW, E, R>(self: Lens<A, ER, EW, RR, RW>, f: (a: A) => Effect.Effect<A, E, R>): Effect.Effect<A, ER | EW | E, RR | RW | R>
|
|
} = Function.dual(2, <A, ER, EW, RR, RW, E, R>(self: Lens<A, ER, EW, RR, RW>, f: (a: A) => Effect.Effect<A, E, R>) =>
|
|
self.modify<A, E, R>(a => Effect.flatMap(
|
|
f(a),
|
|
next => Effect.succeed([next, next] as const),
|
|
)),
|
|
)
|
|
|
|
|
|
Effect.gen(function*() {
|
|
const myChunkRef = yield* SubscriptionRef.make(Chunk.make(12, 38, 69) as Chunk.Chunk<number>)
|
|
const myChunkLens = fromSubscriptionRef(myChunkRef)
|
|
const chunkValueLens = mapEffect(myChunkLens, Chunk.get(1), (a, b) => Effect.succeed(Chunk.replace(a, 1, b)))
|
|
|
|
console.log(yield* myChunkRef.get, yield* chunkValueLens.get)
|
|
// yield* update(myChunkLens, Chunk.replace(1, 22))
|
|
yield* set(chunkValueLens, 22)
|
|
console.log(yield* myChunkRef.get, yield* chunkValueLens.get)
|
|
}).pipe(
|
|
Effect.runSync
|
|
)
|