/* eslint-disable @typescript-eslint/no-explicit-any */ import * as Chunk from "effect/Chunk" import * as Effect from "effect/Effect" import * as Layer from "effect/Layer" import * as Result from "effect/Result" import * as Context from "./Context.js" import { tuple } from "./Function.js" const S1 = Symbol() const S2 = Symbol() const W = Symbol() export interface PureState { readonly [S1]: (_: S) => void readonly [S2]: () => S2 state: S2 } export interface PureLog { readonly [W]: () => W log: Chunk.Chunk } 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: S2 readonly log: Chunk.Chunk constructor(s: S2) { this.state = s this.log = Chunk.empty() } } export function makePureEnv(s: S2): PureEnv { return new PureEnvBase(s) } // 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 } export type Pure = Effect.Effect | R> // type dsl = ProgramDSL export function GMUA_( get: Pure, modify: (i: GA) => Pure, update: (i: GA) => Pure ): Pure { return Effect.flatMap(get, modify).pipe(Effect.flatMap(([s, a]) => Effect.map(update(s), () => a))) } export function GMUA(modify: (i: GA) => Pure) { return ( get: Pure, update: (i: GA) => Pure ) => GMUA_(get, modify, update) } export function GMU_( get: Pure, modify: (i: GA) => Pure, update: (i: GA) => Pure ): Pure { return Effect.flatMap(get, modify).pipe(Effect.flatMap(update)) } export function GMU(modify: (i: GA) => Pure) { return ( get: Pure, update: (i: GA) => Pure ) => GMU_(get, modify, update) } const tagg = Context.Service<{ env: PureEnv }>("PureEnv") function castTag() { return tagg as any as Context.Service, PureEnvEnv> } export const ServiceTag = Symbol() export type ServiceTag = typeof ServiceTag export abstract class PhantomTypeParameter { protected abstract readonly [ServiceTag]: { readonly [NameP in Identifier]: (_: InstantiatedType) => InstantiatedType } } export type ServiceShape> = Omit< T, keyof Context.ServiceClass.Shape > export abstract class ServiceTagged extends PhantomTypeParameter {} export function makeService>(_: Omit) { return _ as T } export const PureEnvEnv = Symbol() export interface PureEnvEnv extends ServiceTagged { env: PureEnv } export function get(): Pure { return (castTag()).useSync((_) => _.env.state) } export function set(s: S): Pure { return (castTag()).useSync((_) => { _.env.state = s }) } export type PureLogT = Pure export function log(w: W): PureLogT { return (castTag()).useSync((_) => { _.env.log = Chunk.append(_.env.log, w) }) } export function logMany(w: Iterable): PureLogT { return (castTag()).useSync((_) => { _.env.log = Chunk.appendAll(_.env.log, Chunk.fromIterable(w)) }) } export function runAll( self: Effect.Effect>, s: S4 ): Effect.Effect< readonly [Chunk.Chunk, Result.Result], never, Exclude }> > { const a = Effect .flatMap(self, (x) => (castTag()) .useSync( ({ env: _ }) => ({ log: _.log, state: _.state }) ) .pipe( Effect.map( ({ log, state }) => tuple(log, Result.succeed(tuple(state, x))) ) )) .pipe(Effect.catch((err: any) => tagg.useSync((env) => tuple(env.env.log, Result.fail(err))))) return Effect.provide(a, Layer.succeed(tagg, { env: makePureEnv(s) as any }) as any) as any } export function runResult( self: Effect.Effect>, s: S4 ) { return Effect.map(runAll(self, s), ([log, r]) => tuple(log, Result.map(r, ([s]) => s))) } export function runTerm( self: Effect.Effect>, s: S4 ) { return Effect.flatMap( runAll(self, s), ([evts, r]) => Effect.map(Effect.fromResult(r), ([s3, a]) => tuple(s3, Chunk.toArray(evts), a)) ) } export function runTermDiscard( self: Effect.Effect>, s: S4 ) { return Effect.map(runTerm(self, s), ([s3, w3]) => tuple(s3, w3)) } export function runA( self: Effect.Effect>, s: S4 ) { return Effect.map(runAll(self, s), ([log, r]) => tuple(log, Result.map(r, ([, a]) => a))) } export function modify( mod: (s: S2) => readonly [S3, A] ): Effect.Effect }> { return (castTag()).useSync((_) => { const [s, a] = mod(_.env.state) _.env.state = s as any return a }) as any } export function modifyM( mod: (s: S2) => Effect.Effect> ): Effect.Effect> { // return serviceWithEffect(_ => Ref.modifyM_(_.state, mod)) return Effect.flatMap( (castTag()).useSync((_) => _), (_: any) => Effect.map(mod(_.env.state), ([s, a]: any) => { _.env.state = s return a }) ) as any } export function updateWith(upd: (s: S2) => S3) { return modify((_: S2) => { const r = upd(_) return tuple(r, r) }) } export function updateWithEffect( upd: (s: S2, log: (evt: W) => PureLogT) => Effect.Effect> ): Effect.Effect> { return modifyM((_: S2) => Effect.map(upd(_, log), (_) => tuple(_, _))) } export type FixEnv = | Exclude> | PureEnvEnv // export function getMA(self: (s: S) => A): Pure { // return Effect.accessM((_: PureState) => Ref.get(_.state).map(self)) // } // 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)) // } // 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.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.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.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): 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.Effect) { // return modifyM(_ => upd(_).map(_ => tuple(_, void 0 as void))) // } // const accessLog = Effect.serviceWithEffect(tag, _ => _.env.log.get) // function runAll( // self: Effect.Effect, E, A>, // s: S4 // ): Effect.Effect}>, never, readonly [Chunk, Either.Either]> { // return self.flatMap(x => // Effect.serviceWithEffect(tag, ({ env: _ }) => // Effect.all({ log: _.log.get, state: _.state.get }) // // Ref.get(_.log).flatMap(log => Ref.get(_.state).map(state => ({ log, state }))) // ).map( // ( // { log, state } // ) => tuple(log, Either.right(tuple(state, x)) as Either.Either) // ) // ).catchAll( // err => // accessLog.map(log => // tuple(log, Either.left(err) as Either.Either) // ) // ).provideService(tag, { env: makePureEnv(s) as any }) as any // } // function runResult( // self: Effect.Effect, E, A>, // s: S4 // ) { // return runAll(self, s).map(([log, r]) => tuple(log, r.map(([s]) => s))) // } // function runA( // self: Effect.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> // export interface ProgramDSL extends dsl_ { } // export function dslmodifyM(dsl: ProgramDSL, mod: (s: S, dsl: ProgramDSL) => Effect.Effect) { // return dsl.modifyM(_ => mod(_, dsl)) // } // export function dslmodify(dsl: ProgramDSL, mod: (s: S, dsl: ProgramDSL) => readonly [S, A]) { // return dsl.modify(_ => mod(_, dsl)) // } // export function dslupdateM(dsl: ProgramDSL, upd: (s: S2, dsl: ProgramDSL) => Effect.Effect) { // return dsl.updateM(_ => upd(_, dsl)) // } // 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.Effect, never, void> // function flatMap(self: Effect.Effect, E, A>, (s: S) => Effect.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.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 } // )