import type { ServiceTagged } from "../service.js" const S1 = Symbol() const S2 = Symbol() const W = Symbol() /** * @tsplus type PureEnv */ export interface PureState { readonly [S1]: (_: S) => void readonly [S2]: () => S2 readonly state: Ref } /** * @tsplus type PureEnv */ export interface PureLog { readonly [W]: () => W readonly log: Ref> } /** * @tsplus type PureEnv */ export interface PureEnv extends PureState, PureLog {} export interface PureEnvTest extends PureState, PureLog {} class PureEnvBase implements PureEnv { readonly [W]!: () => W readonly [S1]!: (_: S) => void readonly [S2]!: () => S2 readonly state: Ref readonly log: Ref> constructor(s: S2) { this.state = Ref.unsafeMake(s) this.log = Ref.unsafeMake(Chunk.empty()) } } export function makePureEnv(s: S2): PureEnv { return new PureEnvBase(s) } /** * @tsplus unify PureEnv */ // eslint-disable-next-line @typescript-eslint/no-explicit-any export function unifyPureEnv>( self: X ): PureEnv< [X] extends [{ [W]: () => infer W }] ? W : never, [X] extends [{ [S1]: (_: infer S) => void }] ? S : never, [X] extends [{ [S2]: () => infer S2 }] ? S2 : never > { // eslint-disable-next-line @typescript-eslint/no-unsafe-return return self } /** * @tsplus type Pure */ export type Pure = Effect | R, E, A> // type dsl = ProgramDSL /** * @tsplus static Pure.Ops GMUA_ */ export function GMUA_( get: Pure, modify: (i: GA) => Pure, update: (i: GA) => Pure ): Pure { return get.flatMap(modify).flatMap(([s, a]) => update(s).map(() => a)) } /** * @tsplus static Pure.Ops GMUA */ export function GMUA(modify: (i: GA) => Pure) { return ( get: Pure, update: (i: GA) => Pure ) => GMUA_(get, modify, update) } /** * @tsplus static Pure.Ops GMU_ */ export function GMU_( get: Pure, modify: (i: GA) => Pure, update: (i: GA) => Pure ): Pure { return get.flatMap(modify).flatMap(update) } /** * @tsplus static Pure.Ops GMU */ export function GMU(modify: (i: GA) => Pure) { return ( get: Pure, update: (i: GA) => Pure ) => GMU_(get, modify, update) } const tagg = Tag<{ env: PureEnv }>() function castTag() { return tagg as any as Tag> } export const PureEnvEnv = Symbol() export interface PureEnvEnv extends ServiceTagged { env: PureEnv } /** * @tsplus static Pure.Ops get */ export function get(): Pure { return castTag().accessWithEffect(_ => _.env.state.get) } /** * @tsplus static Pure.Ops set */ export function set(s: S): Pure { return castTag().accessWithEffect(_ => _.env.state.set(s)) } export type PureLogT = Pure /** * @tsplus static Pure.Ops log */ export function log(w: W): PureLogT { return castTag().accessWithEffect(_ => _.env.log.update(l => l.append(w))) } /** * @tsplus static Pure.Ops logMany */ export function logMany(w: Iterable): PureLogT { return castTag().accessWithEffect(_ => _.env.log.update(l => l.concat(w.toChunk))) } /** * @tsplus static Pure.Ops runAll * @tsplus fluent effect/io/Effect runAll */ export function runAll( self: Effect, E, A>, s: S4 ): Effect }>, never, readonly [Chunk, Either]> { return self.flatMap(x => castTag().accessWithEffect( ({ env: _ }) => Effect.struct({ log: _.log.get, state: _.state.get }) // Ref.get(_.log).flatMap(log => Ref.get(_.state).map(state => ({ log, state }))) ).map( ( { log, state } ) => tuple(log, Either(tuple(state, x)) as Either) ) ).catchAll( err => tagg.accessWithEffect(env => env.env.log.get.map(log => tuple(log, Either.left(err) as Either)) ) ).provideSomeLayer(tagg.makeLayer({ env: makePureEnv(s) as any }) as any) } /** * @tsplus static Pure.Ops runResult * @tsplus fluent effect/io/Effect runResult */ export function runResult( self: Effect, E, A>, s: S4 ) { return runAll(self, s).map(([log, r]) => tuple(log, r.map(([s]) => s))) } /** * @tsplus static Pure.Ops runTerm * @tsplus fluent effect/io/Effect runTerm */ export function runTerm( self: Effect, E, A>, s: S4 ) { return runAll(self, s) .flatMap(([evts, r]) => Effect.fromEither(r) .map(([s3, a]) => tuple(s3, evts.toArray, a)) ) } /** * @tsplus static Pure.Ops runTermDiscard * @tsplus fluent effect/io/Effect runTermDiscard */ export function runTermDiscard( self: Effect, E, A>, s: S4 ) { return self.runTerm(s).map(([s3, w3]) => tuple(s3, w3)) } /** * @tsplus static Pure.Ops runA * @tsplus fluent effect/io/Effect runA */ export function runA( self: Effect, E, A>, s: S4 ) { return runAll(self, s).map(([log, r]) => tuple(log, r.map(([, a]) => a))) } /** * @tsplus static Pure.Ops modifyWith */ export function modify(mod: (s: S2) => readonly [S3, A]): Effect<{ env: PureEnv }, never, A> { return castTag().accessWithEffect( _ => _.env.state.get.map(_ => mod(_)).flatMap(([s, a]) => _.env.state.set(s as any).map(() => a)) ) as any } /** * @tsplus static Pure.Ops modifyWithEffect */ export function modifyM( mod: (s: S2) => Effect, E, readonly [S3, A]> ): Effect, E, A> { // return serviceWithEffect(_ => Ref.modifyM_(_.state, mod)) return castTag().accessWithEffect( _ => _.env.state.get.flatMap(_ => mod(_)).flatMap(([s, a]) => _.env.state.set(s as any).map(() => a)) ) as any } /** * @tsplus static Pure.Ops updateWith */ export function update(upd: (s: S2) => S3) { return modify((_: S2) => { const r = upd(_) return tuple(r, r) }) } export type FixEnv = | Exclude> | PureEnvEnv /** * @tsplus static Pure.Ops updateWithEffect */ export function updateM( upd: (s: S2, log: (evt: W) => PureLogT) => Effect, E, S3> ): Effect, E, S3> { return modifyM((_: S2) => upd(_, Pure.log).map(_ => tuple(_, _))) } // export function getMA(self: (s: S) => A): Pure { // return Effect.accessM((_: PureState) => Ref.get(_.state).map(self)) // } // /** // * @tsplus static Pure.Ops getM // */ // export function getM(self: (s: S) => Pure): Pure { // return Effect.accessM((_: PureState) => Ref.get(_.state).flatMap(self)) // } // export function getTM__(self: (s: S) => Pure) { // return (tag: Tag>) => Effect.accessServiceM(tag)(_ => Ref.get(_.state).flatMap(self)) // } // export function getTM_(tag: Tag>, self: (s: S) => Pure) { // return Effect.accessServiceM(tag)(_ => Ref.get(_.state).flatMap(self)) // } // export function getTM(tag: Tag>) { // const access = Effect.accessServiceM(tag) // return (self: (s: S) => Pure) => access(_ => Ref.get(_.state).flatMap(self)) // } /** * @tsplus type Pure.Ops */ export interface PureOps { $: PureAspects } /** * @tsplus type Pure.Aspects */ export interface PureAspects {} export const Pure: PureOps = { $: {} } // /** // * @tsplus static Pure.Ops makeDSL // */ // export function makeProgramDSL(): ProgramDSL { // return makeDSL_() // } // function makeDSL_() { // const tag = tagg as unknown as Tag> // const get = Effect.serviceWithEffect(tag, _ => _.env.state.get) // function getM(self: (s: S2) => Effect) { return get.flatMap(self) } // function get_(self: (s: S2) => A) { return get.map(self)} // function set(s: S2) { // return Effect.serviceWithEffect(tag, _ => _.env.state.set(s)) // } // function log(w: W2): Effect<{ // env: PureEnv; // }, never, void> { // return Effect.serviceWithEffect(tag, _ => _.env.log.update(l => l.append(w as any))) as any // } // const baseDSL = { // get, // log, // set // } // function modify(mod: (s: S2) => readonly [S3, A]): Effect<{ // env: PureEnv; // }, never, A> { // return Effect.serviceWithEffect(tag, _ => // _.env.state.get.map(_ => mod(_)).flatMap(([s, a]) => _.env.state.set(s as any ).map(() => a)) // ) as any // } // function modifyM(mod: (s: S2) => Effect): Effect<{ // env: PureEnv; // } | R, E, A> { // // return serviceWithEffect(_ => Ref.modifyM_(_.state, mod)) // return Effect.serviceWithEffect(tag, _ => // _.env.state.get.flatMap(_ => mod(_ as unknown as S2)).flatMap(([s, a]) => _.env.state.set(s as any).map(() => a)) // ) as any // } // function update(upd: (s: S2) => S3) { // return modify(_ => tuple(upd(_), void 0 as void)) // } // function updateM(upd: (s: S2) => Effect) { // return modifyM(_ => upd(_).map(_ => tuple(_, void 0 as void))) // } // const accessLog = Effect.serviceWithEffect(tag, _ => _.env.log.get) // function runAll( // self: Effect, E, A>, // s: S4 // ): Effect}>, never, readonly [Chunk, Either]> { // return self.flatMap(x => // Effect.serviceWithEffect(tag, ({ env: _ }) => // Effect.struct({ log: _.log.get, state: _.state.get }) // // Ref.get(_.log).flatMap(log => Ref.get(_.state).map(state => ({ log, state }))) // ).map( // ( // { log, state } // ) => tuple(log, Either(tuple(state, x)) as Either) // ) // ).catchAll( // err => // accessLog.map(log => // tuple(log, Either.left(err) as Either) // ) // ).provideService(tag, { env: makePureEnv(s) as any }) as any // } // function runResult( // self: Effect, E, A>, // s: S4 // ) { // return runAll(self, s).map(([log, r]) => tuple(log, r.map(([s]) => s))) // } // function runA( // self: Effect, E, A>, // s: S4 // ) { // return runAll(self, s).map(([log, r]) => tuple(log, r.map(([, a]) => a))) // } // return { // ...baseDSL, // getM, // get_, // accessLog, // runAll, // runA, // runResult, // modify, // modifyM, // update, // updateM // } // } // type dsl_ = ReturnType> // /** // * @tsplus type Pure/DSL // */ // export interface ProgramDSL extends dsl_ { } // /** // * @tsplus fluent Pure/DSL modifyWithEffect // */ // export function dslmodifyM(dsl: ProgramDSL, mod: (s: S, dsl: ProgramDSL) => Effect) { // return dsl.modifyM(_ => mod(_, dsl)) // } // /** // * @tsplus fluent Pure/DSL modifyWith // */ // export function dslmodify(dsl: ProgramDSL, mod: (s: S, dsl: ProgramDSL) => readonly [S, A]) { // return dsl.modify(_ => mod(_, dsl)) // } // /** // * @tsplus fluent Pure/DSL updateWithEffect // */ // export function dslupdateM(dsl: ProgramDSL, upd: (s: S2, dsl: ProgramDSL) => Effect) { // return dsl.updateM(_ => upd(_, dsl)) // } // /** // * @tsplus fluent Pure/DSL updateWith // */ // export function dslupdate(dsl: ProgramDSL, upd: (s: S, dsl: ProgramDSL) => S) { // return dsl.update(_ => upd(_, dsl)) // } // export interface ZPure {} // export class ZPureImpl implements ZPure { // zipRight( // f: ZPure // ): ZPure { // throw new Error("not implemented") // } // flatMap( // f: (a: A) => ZPure // ): ZPure { // throw new Error("not implemented") // } // } // declare type Eff1 = Effect, never, void> // function flatMap(self: Effect, E, A>, (s: S) => Effect) { // } // export interface PureEnv2 extends PureState2, PureLog {} // const S1 = Symbol() // const S2 = Symbol() // const W = Symbol() // export interface PureState2 { // readonly [S1]: () => S // readonly [S2]: () => S2 // readonly state: Ref // } // const abc = Do($ => { // const s = $(Pure.get<{ a: 1 }>()) // console.log(s) // // const a = $(Pure.log("hello")) // $(Pure.log("hallo" as const)) // $(Pure.log("hello" as const)) // $(Pure.log<"hallo" | "hello" | 5>(5)) // }) // declare const a: Effect< // | { // env: PureEnv // } // | { env: PureEnv } // | { env: PureEnv }, // never, // never // > // type R = _R // type Env = T extends { env: infer rr } ? rr : never // type RRRR = Env // const abcc = unifyPureEnv( // undefined as any as RRRR // ) // const test = Pure.runAll( // abc, // { a: 1 } // )