// ets_tracing: off import "../../Operator/index.js" import { _A, _E, _R } from "@effect-ts/system/Effect" import { AtomicReference } from "@effect-ts/system/Support/AtomicReference" import * as A from "../../Collections/Immutable/Array/index.js" import { pipe } from "../../Function/index.js" import type { Has, Tag } from "../../Has/index.js" import type { Erase, UnionToIntersection } from "../../Utils/index.js" import * as Sy from "../_internal/index.js" export abstract class SyncLayer { readonly hash = new AtomicReference(Symbol()); readonly [_R]!: (_: R) => void; readonly [_E]!: () => E; readonly [_A]!: () => A setKey(key: symbol) { this.hash.set(key) return this } ["+++"](that: SyncLayer): SyncLayer { return new Both(this, that) } ["<<<"]( that: SyncLayer ): SyncLayer & R2, E | E2, A> { return new From(that, this) } [">>>"]( that: SyncLayer ): SyncLayer & R, E | E2, A2> { return new From(this, that) } ["<+<"]( that: SyncLayer ): SyncLayer & R2, E | E2, A & A2> { return new Using(that, this) } [">+>"]( that: SyncLayer ): SyncLayer & R, E | E2, A & A2> { return new Using(this, that) } abstract scope(): Sy.Sync Sy.Sync> build() { const scope = () => this.scope() return Sy.gen(function* (_) { const memo = yield* _(Sy.succeedWith((): SyncMemoMap => new Map())) const scoped = yield* _(scope()) return yield* _(scoped(memo)) }) } } export class Of extends SyncLayer { readonly _tag = "FromSync" constructor(readonly sync: Sy.Sync) { super() } scope(): Sy.Sync Sy.Sync> { return Sy.succeed((_) => this.sync) } } export class Fresh extends SyncLayer { readonly _tag = "Fresh" constructor(readonly sync: SyncLayer) { super() } scope(): Sy.Sync Sy.Sync> { return Sy.succeed((_) => this.sync.build()) } } export class Suspended extends SyncLayer { readonly _tag = "Suspended" constructor(readonly sync: () => SyncLayer) { super() } scope(): Sy.Sync Sy.Sync> { return Sy.succeed(getMemoOrElseCreate(this.sync())) } } export class Both extends SyncLayer { readonly _tag = "Both" constructor( readonly left: SyncLayer, readonly right: SyncLayer ) { super() } scopeBoth(self: Both) { return Sy.succeed((map: SyncMemoMap) => Sy.gen(function* (_) { const l = yield* _(getMemoOrElseCreate(self.left)(map)) const r = yield* _(getMemoOrElseCreate(self.right)(map)) return { ...l, ...r } }) ) } scope(): Sy.Sync< unknown, never, (_: SyncMemoMap) => Sy.Sync > { return this.scopeBoth(this) } } export class Using extends SyncLayer< R & Erase, E | E2, A & A2 > { readonly _tag = "Using" constructor( readonly left: SyncLayer, readonly right: SyncLayer ) { super() } scope(): Sy.Sync< unknown, never, (_: SyncMemoMap) => Sy.Sync, E | E2, A & A2> > { return Sy.succeed((_: SyncMemoMap) => pipe( getMemoOrElseCreate(this.left)(_), Sy.chain((l) => pipe( getMemoOrElseCreate(this.right)(_), Sy.map((r) => ({ ...l, ...r })), Sy.provide(l) ) ) ) ) as any } } export class From extends SyncLayer, E | E2, A2> { readonly _tag = "From" constructor( readonly left: SyncLayer, readonly right: SyncLayer ) { super() } scope(): Sy.Sync< unknown, never, (_: SyncMemoMap) => Sy.Sync, E | E2, A2> > { return Sy.succeed((_: SyncMemoMap) => pipe( getMemoOrElseCreate(this.left)(_), Sy.chain((l) => pipe(getMemoOrElseCreate(this.right)(_), Sy.provide(l))) ) ) as any } } export class All[]> extends SyncLayer< MergeR, MergeE, MergeA > { readonly _tag = "All" constructor(readonly layers: Layers & { 0: SyncLayer }) { super() } scope(): Sy.Sync< unknown, never, (_: 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) => ({ ...k, ...x }))) ) ) ) ) } } 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 type SyncMemoMap = Map export function getMemoOrElseCreate(layer: SyncLayer) { return (m: SyncMemoMap): Sy.Sync => Sy.gen(function* (_) { const inMap = yield* _(Sy.succeedWith(() => 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.succeedWith(() => { m.set(layer.hash.get, a) }) ) return a }) ) } }) } export function fromRawSync(_: Sy.Sync): SyncLayer { return new Of(_) } export function fresh(layer: SyncLayer) { return new Fresh(layer) } export function suspended(layer: () => SyncLayer) { return new Suspended(layer) } export function fromSync(tag: Tag) { return (_: Sy.Sync): SyncLayer> => new Of(pipe(_, Sy.map(tag.has))) } export function fromFunction(tag: Tag) { return (_: (_: R) => T): SyncLayer> => new Of(pipe(Sy.access(_), Sy.map(tag.has))) } export function fromValue(tag: Tag) { return (_: T): SyncLayer> => new Of(Sy.succeed(tag.has(_))) } export function and(left: SyncLayer) { return (right: SyncLayer): SyncLayer => new Both(left, right) } export function andTo(left: SyncLayer) { return ( right: SyncLayer ): SyncLayer, E | E2, A & A2> => new Using(right, left) } export function to(left: SyncLayer) { return ( right: SyncLayer ): SyncLayer, E | E2, A2> => new From(right, left) } export function using(left: SyncLayer) { return ( right: SyncLayer ): SyncLayer & R2, E | E2, A & A2> => new Using(left, right) } export function from(left: SyncLayer) { return ( right: SyncLayer ): SyncLayer & R2, E | E2, A> => new From(left, right) } export function provideSyncLayer(layer: SyncLayer) { return (_: Sy.Sync): Sy.Sync => pipe( layer.build(), Sy.chain((a) => pipe(_, Sy.provide(a))) ) } export function all[]>( ...ls: Ls & { 0: SyncLayer } ): SyncLayer, MergeE, MergeA> { return new All(ls) }