diff --git a/packages/effect-fc/src/Lens.ts b/packages/effect-fc/src/Lens.ts new file mode 100644 index 0000000..3aa73f1 --- /dev/null +++ b/packages/effect-fc/src/Lens.ts @@ -0,0 +1,67 @@ +import { Effect, Predicate, Stream, Subscribable, SubscriptionRef, type Types } from "effect" +import * as Writable from "./Writable.js" + + +export const LensTypeId: unique symbol = Symbol.for("@effect-fc/Lens/Lens") +export type LensTypeId = typeof LensTypeId + +export interface Lens + extends Lens.Variance, Subscribable.Subscribable, Writable.Writable {} + +export declare namespace Lens { + export interface Variance { + readonly [LensTypeId]: { + readonly _A: Types.Invariant + readonly _EG: Types.Invariant + readonly _RG: Types.Invariant + readonly _EW: Types.Invariant + readonly _RW: Types.Invariant + } + } +} + +export const LensPrototype: Writable.WritablePrototype = Object.freeze({ + [LensTypeId]: LensTypeId, + ...Writable.WritablePrototype, +} as const) + + +export const isLens = (u: unknown): u is Lens => Predicate.hasProperty(u, LensTypeId) + + +/** + * Construct a `Lens` from explicit get/changes/set implementations. + */ +export const makeFromGetSet = (options: { + readonly get: Effect.Effect + readonly changes: Stream.Stream + readonly set: (value: A) => Effect.Effect +}): Lens => Object.setPrototypeOf({ + [Subscribable.TypeId]: Subscribable.TypeId, + get: options.get, + changes: options.changes, + modify: (f: (a: A) => readonly [B, A]) => Effect.flatMap(options.get, a => { + const [b, next] = f(a) + return Effect.map(options.set(next), () => b) + }), +}, LensPrototype) as any + + +/** + * Create a `Lens` that directly proxies an `effect` SubscriptionRef. + */ +export const makeFromSubscriptionRef = (ref: SubscriptionRef.SubscriptionRef): Lens => + makeFromGetSet({ + get: ref, + changes: ref.changes, + set: (v: A) => SubscriptionRef.set(ref, v), + }) + + +export const unwrap = ( + effect: Effect.Effect, E1, R1>, +): Lens => makeFromGetSet({ + get: Effect.flatMap(effect, p => p.get), + changes: Stream.unwrap(Effect.map(effect, p => p.changes)), + set: (v: A) => Effect.flatMap(effect, p => p.modify(() => [undefined as void as any, v]) as any), +}) as any diff --git a/packages/effect-fc/src/ProxyRef.ts b/packages/effect-fc/src/ProxyRef.ts deleted file mode 100644 index 9fe7ea9..0000000 --- a/packages/effect-fc/src/ProxyRef.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { Effect, Predicate, Stream, Subscribable, SubscriptionRef, type Types } from "effect" -import * as Writable from "./Writable.js" - - -export const ProxyRefTypeId: unique symbol = Symbol.for("@effect-fc/ProxyRef/ProxyRef") -export type ProxyRefTypeId = typeof ProxyRefTypeId - -export interface ProxyRef - extends ProxyRef.Variance, Subscribable.Subscribable, Writable.Writable {} - -export declare namespace ProxyRef { - export interface Variance { - readonly [ProxyRefTypeId]: { - readonly _A: Types.Invariant - readonly _EG: Types.Invariant - readonly _RG: Types.Invariant - readonly _EW: Types.Invariant - readonly _RW: Types.Invariant - } - } -} - -export const ProxyRefPrototype: Writable.WritablePrototype = Object.freeze({ - [ProxyRefTypeId]: ProxyRefTypeId, - ...Writable.WritablePrototype, -} as const) - - -export const isProxyRef = (u: unknown): u is ProxyRef => Predicate.hasProperty(u, ProxyRefTypeId) - - -/** - * Construct a `ProxyRef` from explicit get/changes/set implementations. - */ -export const makeFromGetSet = (options: { - readonly get: Effect.Effect - readonly changes: Stream.Stream - readonly set: (value: A) => Effect.Effect -}): ProxyRef => Object.setPrototypeOf({ - [Subscribable.TypeId]: Subscribable.TypeId, - get: options.get, - changes: options.changes, - modify: (f: (a: A) => readonly [B, A]) => Effect.flatMap(options.get, a => { - const [b, next] = f(a) - return Effect.map(options.set(next), () => b) - }), -}, ProxyRefPrototype) as any - - -/** - * Create a `ProxyRef` that directly proxies an `effect` SubscriptionRef. - */ -export const makeFromSubscriptionRef = (ref: SubscriptionRef.SubscriptionRef): ProxyRef => - makeFromGetSet({ - get: ref, - changes: ref.changes, - set: (v: A) => SubscriptionRef.set(ref, v), - }) - - -export const unwrap = ( - effect: Effect.Effect, E1, R1>, -): ProxyRef => makeFromGetSet({ - get: Effect.flatMap(effect, p => p.get), - changes: Stream.unwrap(Effect.map(effect, p => p.changes)), - set: (v: A) => Effect.flatMap(effect, p => p.modify(() => [undefined as void as any, v]) as any), -}) as any diff --git a/packages/effect-fc/src/index.ts b/packages/effect-fc/src/index.ts index 681c3e9..9d0c561 100644 --- a/packages/effect-fc/src/index.ts +++ b/packages/effect-fc/src/index.ts @@ -2,10 +2,10 @@ export * as Async from "./Async.js" export * as Component from "./Component.js" export * as ErrorObserver from "./ErrorObserver.js" export * as Form from "./Form.js" +export * as Lens from "./Lens.js" export * as Memoized from "./Memoized.js" export * as Mutation from "./Mutation.js" export * as PropertyPath from "./PropertyPath.js" -export * as ProxyRef from "./ProxyRef.js" export * as PubSub from "./PubSub.js" export * as Query from "./Query.js" export * as QueryClient from "./QueryClient.js"