import type { Erase, UnionToIntersection } from "@principia/prelude/Utils"; import { inspect } from "util"; import * as A from "../../Array"; import { pipe } from "../../Function"; import type { Has, Tag } from "../../Has"; import * as Sy from "./_internal"; import type { SyncMemoMap } from "./model"; import { SyncLayer, SyncLayerInstructionTag } from "./model"; export const getMemoOrElseCreate = (layer: SyncLayer) => (m: SyncMemoMap): Sy.Sync => Sy.gen(function* (_) { const inMap = yield* _(Sy.total(() => m.get(layer.hash.get))); if (inMap) { return yield* _(Sy.succeed(inMap)); } else { return yield* _( Sy.gen(function* (_) { const f = yield* _(layer.scope()); const a = yield* _(f(m)); yield* _( Sy.total(() => { m.set(layer.hash.get, a); }) ); return a; }) ); } }); export class FromSyncInstruction extends SyncLayer { readonly _tag = SyncLayerInstructionTag.FromSync; constructor(readonly sync: Sy.Sync) { super(); } scope(): Sy.IO<(_: SyncMemoMap) => Sy.Sync> { return Sy.succeed(() => this.sync); } } export class FreshInstruction extends SyncLayer { readonly _tag = SyncLayerInstructionTag.Fresh; constructor(readonly layer: SyncLayer) { super(); } scope(): Sy.IO<(_: SyncMemoMap) => Sy.Sync> { return Sy.succeed(getMemoOrElseCreate(this.layer)); } } export class SuspendInstruction extends SyncLayer { readonly _tag = SyncLayerInstructionTag.Suspend; constructor(readonly factory: () => SyncLayer) { super(); } scope(): Sy.IO<(_: SyncMemoMap) => Sy.Sync> { return Sy.succeed(getMemoOrElseCreate(this.factory())); } } export class BothInstruction extends SyncLayer { readonly _tag = SyncLayerInstructionTag.Both; constructor(readonly left: SyncLayer, readonly right: SyncLayer) { super(); } scopeBoth(self: BothInstruction) { return Sy.succeed((memo: SyncMemoMap) => Sy.gen(function* (_) { const l = yield* _(getMemoOrElseCreate(self.left)(memo)); const r = yield* _(getMemoOrElseCreate(self.right)(memo)); return { ...l, ...r }; }) ); } scope(): Sy.IO<(_: SyncMemoMap) => Sy.Sync> { return this.scopeBoth(this); } } export class UsingInstruction extends SyncLayer, E | E1, A & A1> { readonly _tag = SyncLayerInstructionTag.Using; constructor(readonly left: SyncLayer, readonly right: SyncLayer) { super(); } scope(): Sy.Sync Sy.Sync, E | E1, A & A1>> { return Sy.succeed((_) => pipe( getMemoOrElseCreate(this.left)(_), Sy.chain((l) => pipe( getMemoOrElseCreate(this.right)(_), Sy.map((r) => ({ ...l, ...r })), Sy.give(l) ) ) ) ); } } export class FromInstruction extends SyncLayer, E | E1, A1> { readonly _tag = SyncLayerInstructionTag.From; constructor(readonly left: SyncLayer, readonly right: SyncLayer) { super(); } scope(): Sy.IO<(_: SyncMemoMap) => Sy.Sync, E | E1, A1>> { return Sy.succeed((_) => pipe( getMemoOrElseCreate(this.left)(_), Sy.chain((l) => pipe(getMemoOrElseCreate(this.right)(_), Sy.give(l))) ) ); } } export type MergeR>> = UnionToIntersection< { [k in keyof Ls]: [Ls[k]] extends [SyncLayer] ? (unknown extends X ? never : X) : never; }[number] >; export type MergeE>> = { [k in keyof Ls]: [Ls[k]] extends [SyncLayer] ? X : never; }[number]; export type MergeA>> = UnionToIntersection< { [k in keyof Ls]: [Ls[k]] extends [SyncLayer] ? (unknown extends X ? never : X) : never; }[number] >; export class AllInstruction>> extends SyncLayer< MergeR, MergeE, MergeA > { readonly _tag = SyncLayerInstructionTag.All; constructor(readonly layers: Layers & { 0: SyncLayer }) { super(); } scope(): Sy.IO<(_: SyncMemoMap) => Sy.Sync, MergeE, MergeA>> { return Sy.succeed((_) => pipe( this.layers, A.reduce(>Sy.succeed({}), (b, a) => pipe( getMemoOrElseCreate(a)(_), Sy.chain((x) => Sy.map_(b, (k) => ({ ...x, ...k }))) ) ) ) ); } } export const fromRawSync = (sync: Sy.Sync): SyncLayer => new FromSyncInstruction(sync); export const fresh = (layer: SyncLayer) => new FreshInstruction(layer); export const suspend = (layer: () => SyncLayer) => new SuspendInstruction(layer); export const fromSync = (tag: Tag) => (_: Sy.Sync): SyncLayer> => new FromSyncInstruction(pipe(_, Sy.map(tag.of))); export const fromFunction = (tag: Tag) => (f: (_: R) => T): SyncLayer> => new FromSyncInstruction(pipe(Sy.asks(f), Sy.map(tag.of))); export const fromValue = (tag: Tag) => (_: T): SyncLayer> => new FromSyncInstruction(Sy.succeed(tag.of(_))); export const and = (left: SyncLayer) => ( right: SyncLayer ): SyncLayer => new BothInstruction(left, right); export const andTo = (left: SyncLayer) => ( right: SyncLayer ): SyncLayer, E | E2, A & A2> => new UsingInstruction(right, left); export const to = (left: SyncLayer) => ( right: SyncLayer ): SyncLayer, E | E2, A2> => new FromInstruction(right, left); export const using = (left: SyncLayer) => ( right: SyncLayer ): SyncLayer & R2, E | E2, A & A2> => new UsingInstruction(left, right); export const from = (left: SyncLayer) => ( right: SyncLayer ): SyncLayer & R2, E | E2, A> => new FromInstruction(left, right); export const giveLayer = (layer: SyncLayer) => ( _: Sy.Sync ): Sy.Sync => pipe( layer.build(), Sy.chain((a) => pipe(_, Sy.give(a))) ); export const all = >>( ...ls: Ls & { 0: SyncLayer } ): SyncLayer, MergeE, MergeA> => new AllInstruction(ls);