import { Effect, Pipeable, Predicate, Readable, Stream, Subscribable, type SubscriptionRef } from "effect" export const LensTypeId: unique symbol = Symbol.for("@effect-fc/Lens/Lens") export type LensTypeId = typeof LensTypeId export interface Lens extends Subscribable.Subscribable, LensPrototype { readonly modify: ( f: (a: A) => Effect.Effect ) => Effect.Effect } export interface LensPrototype extends Pipeable.Pipeable { readonly [LensTypeId]: LensTypeId } export const LensPrototype: LensPrototype = Object.freeze({ ...Pipeable.Prototype, [Readable.TypeId]: Readable.TypeId, [Subscribable.TypeId]: Subscribable.TypeId, [LensTypeId]: LensTypeId, } as const) export const isLens = (u: unknown): u is Lens => Predicate.hasProperty(u, LensTypeId) export const make = ( options: { readonly get: Effect.Effect readonly changes: Stream.Stream } & ( | { readonly set: (value: A) => Effect.Effect } | { readonly modify: (f: (a: A) => Effect.Effect) => Effect.Effect } ) ): Lens => Object.setPrototypeOf({ get: options.get, changes: options.changes, modify: Predicate.hasProperty(options, "modify") ? options.modify : (f: (a: A) => Effect.Effect) => Effect.flatMap(options.get, a => Effect.flatMap(f(a), ([b, next]) => Effect.map(options.set(next), () => b) ) ), }, LensPrototype) /** * Creates a `Lens` that directly proxies a `SubscriptionRef`. */ export const fromSubscriptionRef = ( ref: SubscriptionRef.SubscriptionRef ): Lens => make({ get: ref.get, changes: ref.changes, modify: ref.modifyEffect, }) export const unwrap = ( effect: Effect.Effect, E1, R1> ): Lens => make({ get: Effect.flatMap(effect, l => l.get), changes: Stream.unwrap(Effect.map(effect, l => l.changes)), modify: ( f: (a: A) => Effect.Effect ) => Effect.flatMap(effect, l => l.modify(f)), })