/** * @typed/fp/FxT is a transformer for creating Do-notation compatible versions of * other data types. * * @since 0.13.0 */ import { Apply, Apply1, Apply2, Apply3, Apply4 } from 'fp-ts/Apply' import { Either, left, match, right } from 'fp-ts/Either' import { FromReader, FromReader2, FromReader3, FromReader3C, FromReader4 } from 'fp-ts/FromReader' import { flow, identity, pipe } from 'fp-ts/function' import { HKT, HKT2, Kind, Kind2, Kind3, Kind4, URIS, URIS2, URIS3, URIS4 } from 'fp-ts/HKT' import { NaturalTransformation, NaturalTransformation11, NaturalTransformation12, NaturalTransformation12C, NaturalTransformation13, NaturalTransformation13C, NaturalTransformation14, NaturalTransformation14C, NaturalTransformation21, NaturalTransformation22, NaturalTransformation22C, NaturalTransformation23, NaturalTransformation23C, NaturalTransformation23R, NaturalTransformation24, NaturalTransformation24R, NaturalTransformation24S, NaturalTransformation33, NaturalTransformation34, } from 'fp-ts/NaturalTransformation' import { Pointed, Pointed1, Pointed2, Pointed3, Pointed4 } from 'fp-ts/Pointed' import { A, U } from 'ts-toolbelt' import { Arity1 } from './function' import { chain as chain_, doFx, Fx, map as map_ } from './Fx' import { ApplyVariance, Hkt, Initial } from './HKT' import { MonadRec, MonadRec1, MonadRec2, MonadRec2C, MonadRec3, MonadRec4 } from './MonadRec' import { ProvideAll, ProvideAll2, ProvideAll3, ProvideAll4, ProvideSome, ProvideSome2, ProvideSome3, ProvideSome4, UseAll, UseAll2, UseAll3, UseAll4, UseSome, UseSome2, UseSome3, UseSome4, } from './Provide' /** * @category Model * @since 0.13.0 */ export type FxT = Params extends readonly [...infer Init, infer R] ? Fx, R> : never /** * @category Type-level * @since 0.13.0 */ export type LiftFx = F extends URIS ? LiftFx1 : F extends URIS2 ? LiftFx2 : F extends URIS3 ? LiftFx3 : F extends URIS4 ? LiftFx4 : LiftFxHKT /** * @category Type-level * @since 0.13.0 */ export type LiftFx1 = (kind: Kind) => Fx, A> /** * @category Type-level * @since 0.13.0 */ export type LiftFx2 = ( kind: Kind2, A>, ) => Fx, A>, A> type IsNever = A.Equals<[T], [never]> extends 1 ? true : false type OrUnknown = IsNever extends true ? unknown : T /** * @category Type-level * @since 0.13.0 */ export type LiftFx3 = ( kind: Kind3, E, A>, ) => Fx, E, A>, A> /** * @category Type-level * @since 0.13.0 */ export type LiftFx4 = ( kind: Kind4, E, A>, ) => Fx, E, A>, A> /** * @category Type-level * @since 0.13.0 */ export type LiftFxHKT = (kind: HKT) => Fx, A> /** * Create a lift function that will convert any F into Fx, A> * @category Constructor * @since 0.13.0 */ export const liftFx = () => lift_ as LiftFx function lift_(hkt: HKT): Fx { return { [Symbol.iterator]: function* () { const value: unknown = yield hkt return value as A }, } } /** * @category Lift * @since 0.13.0 */ export function getDo(): , R, N = unknown>( f: (lift: LiftFx1) => Generator, ) => Fx export function getDo(): , R, N = unknown>( f: (lift: LiftFx2) => Generator, ) => Fx export function getDo(): < Y extends Kind3, Z, N = unknown, >( f: (lift: LiftFx3) => Generator, ) => Fx export function getDo(): < Y extends Kind4, Z, N = unknown, >( f: (lift: LiftFx4) => Generator, ) => Fx export function getDo(): , R, N = unknown>( f: (lift: LiftFxHKT) => Generator, ) => Fx export function getDo() { const lift = liftFx() return , R, N = unknown>( f: (lift: LiftFxHKT) => Generator, ): Fx => doFx(() => f(lift as LiftFxHKT)) } type Kind2E = [E] extends [Kind2] ? R : never type Kind3E = [E] extends [Kind3] ? R : never type Kind3R = [E] extends [Kind3] ? R : never type Kind4E = [E] extends [Kind4] ? R : never type Kind4R = [E] extends [Kind4] ? R : never type Kind4S = [E] extends [Kind4] ? R : never /** * @category Interpreter * @since 0.13.0 */ export function toMonad( M: MonadRec1, ): , R>(fx: Fx) => Kind export function toMonad( M: MonadRec2, ): , R>( fx: Fx, ) => Kind2>>, R> export function toMonad( M: MonadRec2C, ): , R>(fx: Fx) => Kind2 export function toMonad( M: MonadRec3, ): , R>( fx: Fx, ) => Kind3< F, ApplyVariance>>, ApplyVariance>>, R > export function toMonad( M: MonadRec4, ): , R>( fx: Fx, ) => Kind4< F, ApplyVariance>>, ApplyVariance>>, ApplyVariance>>, R > export function toMonad(M: MonadRec): (fx: Fx) => HKT /** * Using a ChainRec instance we can create a stack-safe interpreter for do-notation * using Fx + generators. */ export function toMonad(M: MonadRec) { return function fxToMonad, R>(fx: Fx): HKT { return pipe( M.of(fx[Symbol.iterator]), M.map((f) => f()), M.chain((generator) => { const result = generator.next() if (result.done) { return M.of(result.value) } return pipe( result.value, M.chain( M.chainRec((a) => { const result = generator.next(a) return result.done ? pipe(result.value, right, M.of) : pipe(result.value, M.map(left)) }), ), ) }), ) } } /** * @category Combinator * @since 0.13.0 */ export function map(): ( f: (value: A) => B, ) => (fx: FxT) => FxT export function map(): ( f: (value: A) => B, ) => (fx: FxT) => FxT export function map(): ( f: (value: A) => B, ) => (fx: FxT) => FxT export function map(): ( f: (value: A) => B, ) => (fx: FxT) => FxT export function map(): (f: (value: A) => B) => (fx: FxT) => FxT export function map() { return map_ } /** * @category Combinator * @since 0.13.0 */ export function chain(): ( f: (value: A) => FxT, ) => (fx: FxT) => FxT]> export function chain(): ( f: (value: A) => FxT, ) => (fx: FxT) => FxT, B]> export function chain(): ( f: (value: A) => FxT, ) => ( fx: FxT, ) => FxT< F, [ApplyVariance, ApplyVariance, ApplyVariance] > export function chain(): ( f: (value: A) => FxT, ) => ( fx: FxT, ) => FxT< F, [ ApplyVariance, ApplyVariance, ApplyVariance, ApplyVariance, ] > export function chain(): (f: (value: A) => FxT) => (fx: FxT) => FxT export function chain() { return chain_ } /** * @category Combinator * @since 0.13.0 */ export function ap( M: MonadRec1 & Apply1, ): (fa: FxT) => (fab: FxT]>) => FxT export function ap( M: MonadRec2 & Apply2, ): ( fa: FxT, ) => (fab: FxT]>) => FxT, B]> export function ap( M: MonadRec2 & Apply2, ): (fa: FxT) => (fab: FxT]>) => FxT export function ap( M: MonadRec3 & Apply3, ): ( fa: FxT, ) => ( fab: FxT]>, ) => FxT, ApplyVariance, B]> export function ap( M: MonadRec4 & Apply4, ): ( fa: FxT, ) => ( fab: FxT]>, ) => FxT< F, [ ApplyVariance, ApplyVariance, ApplyVariance, B, ] > export function ap( M: MonadRec & Apply, ): (fa: FxT) => (fab: FxT]>) => FxT export function ap(M: MonadRec & Apply) { const lift = liftFx() const to = toMonad(M) return (fa: FxT) => (fab: FxT]>): FxT => pipe(fab, to, pipe(fa, to, M.ap), lift) as FxT } /** * @category Combinator * @since 0.13.0 */ export function chainRec(M: MonadRec1): ChainRecFxT export function chainRec(M: MonadRec2): ChainRecFxT export function chainRec(M: MonadRec3): ChainRecFxT export function chainRec(M: MonadRec4): ChainRecFxT export function chainRec(M: MonadRec): ChainRecFxT { return chainRec_(M) as ChainRecFxT } /** * @category Type-level * @since 0.13.0 */ export type ChainRecFxT = F extends URIS2 ? (f: Arity1]>>) => (a: A) => FxT : F extends URIS3 ? (f: Arity1]>>) => (a: A) => FxT : F extends URIS4 ? (f: Arity1]>>) => (a: A) => FxT : (f: Arity1]>>) => (a: A) => FxT function chainRec_(M: MonadRec) { const to = toMonad(M) const lift = liftFx() return (f: Arity1]>>) => (a: A): FxT => { const fbm = pipe( f(a), to, M.chain( match( M.chainRec((a) => pipe(a, f, to)), M.of, ), ), ) return lift(fbm) as FxT } } /** * @category Constructor * @since 0.13.0 */ export function of(M: Pointed1): (value: A) => Fx, A> export function of( M: Pointed2, ): >(value: A) => Fx, A> export function of( M: Pointed3, ): , E = Initial>(value: A) => Fx, A> export function of( M: Pointed4, ): , R = Initial, E = Initial>( value: A, ) => Fx, A> export function of(M: Pointed): (value: A) => Fx, A> export function of(M: Pointed) { const lift = liftFx() return (value: A) => lift(M.of(value)) } /** * @category Constructor * @since 0.13.0 */ export function ask(M: FromReader2): () => Fx, A> export function ask( M: FromReader3, ): () => Fx, A]>, A> export function ask(M: FromReader3C): () => Fx, A> export function ask( M: FromReader4, ): () => Fx, A, Initial, A]>, A> export function ask(M: FromReader): () => Fx, A> export function ask(M: FromReader) { const lift = liftFx() return () => lift(M.fromReader(identity)) } /** * @category Combinator * @since 0.13.0 */ export function useSome( M: UseSome2 & MonadRec2, ): (provided: A) => (fx: Fx, T>) => Fx, T> export function useSome( M: UseSome3 & MonadRec3, ): ( provided: A, ) => (fx: Fx, T>) => Fx, T> export function useSome( M: UseSome4 & MonadRec4, ): ( provided: A, ) => (fx: Fx, T>) => Fx, T> export function useSome( M: UseSome & MonadRec, ): (provided: A) => (fx: Fx, T>) => Fx, T> export function useSome(M: UseSome & MonadRec) { const lift = liftFx() const to = toMonad(M) return (provided: A) => (fx: Fx, R>) => pipe(fx, to, (x) => M.useSome(provided)(x as any), lift) } /** * @category Combinator * @since 0.13.0 */ export function provideSome( M: ProvideSome2 & MonadRec2, ): (provided: A) => (fx: Fx, T>) => Fx, T> export function provideSome( M: ProvideSome3 & MonadRec3, ): ( provided: A, ) => (fx: Fx, T>) => Fx, T> export function provideSome( M: ProvideSome4 & MonadRec4, ): ( provided: A, ) => (fx: Fx, T>) => Fx, T> export function provideSome( M: ProvideSome & MonadRec, ): (provided: A) => (fx: Fx, T>) => Fx, T> export function provideSome(M: ProvideSome & MonadRec) { const lift = liftFx() const to = toMonad(M) return (provided: A) => (fx: Fx, R>) => pipe(fx, to, (x) => M.provideSome(provided)(x as any), lift) } /** * @category Combinator * @since 0.13.0 */ export function useAll( M: UseAll2 & MonadRec2, ): (provided: A) => (fx: Fx, T>) => Fx, T> export function useAll( M: UseAll3 & MonadRec3, ): ( provided: A, ) => (fx: Fx, T>) => Fx, T> export function useAll( M: UseAll4 & MonadRec4, ): ( provided: A, ) => (fx: Fx, T>) => Fx, T> export function useAll( M: UseAll & MonadRec, ): (provided: A) => (fx: Fx, T>) => Fx, T> export function useAll(M: UseAll & MonadRec) { const lift = liftFx() const to = toMonad(M) return (provided: A) => (fx: Fx, R>) => pipe(fx, to, (x) => M.useAll(provided)(x as any), lift) } /** * @category Combinator * @since 0.13.0 */ export function provideAll( M: ProvideAll2 & MonadRec2, ): (provided: A) => (fx: Fx, T>) => Fx, T> export function provideAll( M: ProvideAll3 & MonadRec3, ): ( provided: A, ) => (fx: Fx, T>) => Fx, T> export function provideAll( M: ProvideAll4 & MonadRec4, ): ( provided: A, ) => (fx: Fx, T>) => Fx, T> export function provideAll( M: ProvideAll & MonadRec, ): (provided: A) => (fx: Fx, T>) => Fx, T> export function provideAll(M: ProvideAll & MonadRec) { const lift = liftFx() const to = toMonad(M) return (provided: A) => (fx: Fx, R>) => pipe(fx, to, (x) => M.provideAll(provided)(x as any), lift) } /** * @category Natural Transformation * @since 0.13.0 */ export function fromNaturalTransformation( transformation: NaturalTransformation11, ): (fa: Kind) => Fx, A> export function fromNaturalTransformation( transformation: NaturalTransformation12, ): >(fa: Kind) => Fx, A> export function fromNaturalTransformation( transformation: NaturalTransformation12C, ): (fa: Kind) => Fx, A> export function fromNaturalTransformation( transformation: NaturalTransformation13, ): , E = Initial>(fa: Kind) => Fx, A> export function fromNaturalTransformation( transformation: NaturalTransformation13C, ): >(fa: Kind) => Fx, A> export function fromNaturalTransformation( transformation: NaturalTransformation14, ): , R = Initial, E = Initial>( fa: Kind, ) => Fx, A> export function fromNaturalTransformation( transformation: NaturalTransformation14C, ): , R = Initial>(fa: Kind) => Fx, A> export function fromNaturalTransformation( transformation: NaturalTransformation21, ): (fa: Kind2) => Fx, A> export function fromNaturalTransformation( transformation: NaturalTransformation22, ): (fa: Kind2) => Fx, A> export function fromNaturalTransformation( transformation: NaturalTransformation22C, ): (fa: Kind2) => Fx, A> export function fromNaturalTransformation( transformation: NaturalTransformation23 | NaturalTransformation23R, ): >(fa: Kind2) => Fx, A> export function fromNaturalTransformation( transformation: NaturalTransformation23C, ): >(fa: Kind2) => Fx, A> export function fromNaturalTransformation( transformation: | NaturalTransformation24 | NaturalTransformation24R | NaturalTransformation24S, ): , R = Initial>( fa: Kind2, ) => Fx, A> export function fromNaturalTransformation( transformation: NaturalTransformation33, ): (fa: Kind3) => Fx, A> export function fromNaturalTransformation( transformation: NaturalTransformation34, ): >(fa: Kind3) => Fx, A> export function fromNaturalTransformation( transformation: NaturalTransformation, ): (fa: HKT) => Fx, A> export function fromNaturalTransformation( transformation: NaturalTransformation, ): (fa: HKT) => Fx, A> { return flow(transformation, liftFx()) as any }