import type { UnionToIntersection } from "@principia/prelude/Utils"; import * as A from "../Array"; import { pipe } from "../Function"; import type { Has, Region, Tag } from "../Has"; import { mergeEnvironments, tag } from "../Has"; import * as R from "../Record"; import * as X from "../XPure"; import type { Sync } from "./model"; /** * Access a record of services with the required Service Entries */ export const asksServicesM = >>(s: SS) => ( f: ( a: { [k in keyof SS]: [SS[k]] extends [Tag] ? T : unknown; } ) => Sync ) => X.asksM((r: UnionToIntersection<{ [k in keyof SS]: [SS[k]] extends [Tag] ? Has : unknown }[keyof SS]>) => f(R.map_(s, (v) => r[v.key]) as any) ); export const asksServicesTM = []>(...s: SS) => ( f: ( ...a: { [k in keyof SS]: [SS[k]] extends [Tag] ? T : unknown; } ) => Sync ) => X.asksM( ( r: UnionToIntersection< { [k in keyof SS]: [SS[k]] extends [Tag] ? Has : never; }[keyof SS & number] > ) => f(...(A.map_(s, (v) => r[v.key]) as any)) ); export const asksServicesT = []>(...s: SS) => ( f: ( ...a: { [k in keyof SS]: [SS[k]] extends [Tag] ? T : unknown; } ) => B ) => X.asks( ( r: UnionToIntersection< { [k in keyof SS]: [SS[k]] extends [Tag] ? Has : never; }[keyof SS & number] > ) => f(...(A.map_(s, (v) => r[v.key]) as any)) ); /** * Access a record of services with the required Service Entries */ export const asksServices = >>(s: SS) => ( f: ( a: { [k in keyof SS]: [SS[k]] extends [Tag] ? T : unknown; } ) => B ) => X.asks((r: UnionToIntersection<{ [k in keyof SS]: [SS[k]] extends [Tag] ? Has : unknown }[keyof SS]>) => f(R.map_(s, (v) => r[v.key]) as any) ); /** * Access a service with the required Service Entry */ export const asksServiceM = (s: Tag) => (f: (a: T) => Sync) => X.asksM((r: Has) => f(r[s.key as any])); /** * Access a service with the required Service Entry */ export const asksServiceF = (s: Tag) => < K extends keyof T & { [k in keyof T]: T[k] extends (...args: any[]) => Sync ? k : never; }[keyof T] >( k: K ) => ( ...args: T[K] extends (...args: infer ARGS) => Sync ? ARGS : unknown[] ): T[K] extends (...args: any[]) => Sync ? Sync, E, A> : unknown[] => asksServiceM(s)((t) => (t[k] as any)(...args)) as any; /** * Access a service with the required Service Entry */ export const asksService = (s: Tag) => (f: (a: T) => B) => asksServiceM(s)((a) => X.pure(f(a))); /** * Access a service with the required Service Entry */ export const askService = (s: Tag) => asksServiceM(s)((a) => X.pure(a)); /** * Provides the service with the required Service Entry, depends on global HasRegistry */ export const giveServiceM = (_: Tag) => (f: Sync) => ( ma: Sync, E1, A1> ): Sync => X.asksM((r: R & R1) => X.chain_(f, (t) => X.giveAll_(ma, mergeEnvironments(_, r, t)))); /** * Provides the service with the required Service Entry, depends on global HasRegistry */ export const giveService = (_: Tag) => (f: T) => (ma: Sync, E1, A1>): Sync => giveServiceM(_)(X.pure(f))(ma); /** * Replaces the service with the required Service Entry, depends on global HasRegistry */ export const replaceServiceM = (_: Tag, f: (_: T) => Sync) => ( ma: Sync, E1, A1> ): Sync, E | E1, A1> => asksServiceM(_)((t) => giveServiceM(_)(f(t))(ma)); /** * Replaces the service with the required Service Entry, depends on global HasRegistry */ export const replaceServiceM_ = ( ma: Sync, E1, A1>, _: Tag, f: (_: T) => Sync ): Sync, E | E1, A1> => asksServiceM(_)((t) => giveServiceM(_)(f(t))(ma)); /** * Replaces the service with the required Service Entry, depends on global HasRegistry */ export const replaceService = (_: Tag, f: (_: T) => T) => ( ma: Sync, E1, A1> ): Sync, E1, A1> => asksServiceM(_)((t) => giveServiceM(_)(X.pure(f(t)))(ma)); /** * Replaces the service with the required Service Entry, depends on global HasRegistry */ export const replaceService_ = ( ma: Sync, E1, A1>, _: Tag, f: (_: T) => T ): Sync, E1, A1> => asksServiceM(_)((t) => giveServiceM(_)(X.pure(f(t)))(ma)); export const region = (): Tag> => tag>(); export const useRegion = (h: Tag>) => (e: Sync) => asksServiceM(h)((a) => pipe(e, X.give((a as any) as T))); export const asksRegionM = (h: Tag>) => (e: (_: T) => Sync) => asksServiceM(h)((a) => pipe(X.asksM(e), X.give((a as any) as T))); export const asksRegion = (h: Tag>) => (e: (_: T) => A) => asksServiceM(h)((a) => pipe(X.asks(e), X.give((a as any) as T))); export const askRegion = (h: Tag>) => asksServiceM(h)((a) => pipe( X.asks((r: T) => r), X.give((a as any) as T) ) ); export const askServiceIn = (_: Tag) => (h: Tag & T, K>>) => useRegion(h)( asksServiceM(_)((a) => pipe( X.asks((r: A) => r), X.give((a as any) as A) ) ) ); export const asksServiceIn = (_: Tag) => (h: Tag & T, K>>) => (f: (_: A) => B) => useRegion(h)( asksServiceM(_)((a) => pipe( X.asks((r: A) => f(r)), X.give((a as any) as A) ) ) ); export const asksServiceInM = (_: Tag) => (h: Tag & T, K>>) => ( f: (_: A) => Sync ) => useRegion(h)( asksServiceM(_)((a) => pipe( X.asksM((r: A) => f(r)), X.give((a as any) as A) ) ) ); /** * ```haskell * asService :: Tag a -> Task r e a -> Task r e (Has a) * ``` * * Maps the success value of this effect to a service. */ export const asService = (has: Tag) => (fa: Sync) => X.map_(fa, has.of);