import type { ApFn, ApFn_ } from './Apply' import type { Functor2, FunctorMin } from './Functor' import type { CrossFn_, Semimonoidal, SemimonoidalMin } from './Semimonoidal' import type { EnforceNonEmptyRecord } from './util/types' import { Functor, getFunctorComposition } from './Functor' import * as HKT from './HKT' import { tuple } from './tuple' export interface SemimonoidalFunctor extends Functor, Semimonoidal { readonly crossWith_: CrossWithFn_ readonly crossWith: CrossWithFn } export type SemimonoidalFunctorMin = | (SemimonoidalMin & FunctorMin) | ({ readonly crossWith_: CrossWithFn_ } & FunctorMin) | ({ readonly crossWith_: CrossWithFn_ } & SemimonoidalMin & FunctorMin) export function SemimonoidalFunctor( F: SemimonoidalFunctorMin ): SemimonoidalFunctor { let cross_: CrossFn_ let crossWith_: CrossWithFn_ if ('crossWith_' in F) { if ('cross_' in F) { cross_ = F.cross_ crossWith_ = F.crossWith_ } else { crossWith_ = F.crossWith_ cross_ = (fa, fb) => crossWith_(fa, fb, (a, b) => [a, b]) } } else { cross_ = F.cross_ crossWith_ = (fa, fb, f) => F.map_(cross_(fa, fb), (ab) => f(ab[0], ab[1])) } return HKT.instance>({ ...Functor(F), cross_, cross: (fb) => (fa) => cross_(fa, fb), crossWith_, crossWith: (fb, f) => (fa) => crossWith_(fa, fb, f) }) } export interface SemimonoidalFunctor2 extends Functor2 { readonly crossWith_: CrossWithFn2_ readonly crossWith: CrossWithFn2 } export function getSemimonoidalFunctorComposition( F: SemimonoidalFunctor, G: SemimonoidalFunctor ): SemimonoidalFunctor2 { const crossWith_: SemimonoidalFunctor2['crossWith_'] = (fga, fgb, f) => F.crossWith_(fga, fgb, (ga, gb) => G.crossWith_(ga, gb, f)) return HKT.instance({ ...getFunctorComposition(F, G), crossWith_, crossWith: (fgb, f) => (fga) => crossWith_(fga, fgb, f) }) } export interface CrossWithFn_ { ( fa: HKT.Kind, fb: HKT.Kind< F, TC, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, B >, f: (a: A, b: B) => C ): HKT.Kind< F, TC, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, C > } export interface CrossWithFn { ( fb: HKT.Kind, f: (a: A, b: B) => C ): ( fa: HKT.Kind< F, TC, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, A > ) => HKT.Kind< F, TC, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, C > } export interface CrossWithFn2_ { < NF extends string, KF, QF, WF, XF, IF, SF, RF, EF, NG extends string, KG, QG, WG, XG, IG, SG, RG, EG, NF1 extends string, KF1, QF1, WF1, XF1, IF1, SF1, RF1, EF1, NG1 extends string, KG1, QG1, WG1, XG1, IG1, SG1, RG1, EG1, A, B, C >( fga: HKT.Kind>, fgb: HKT.Kind< F, TCF, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Kind< G, TCG, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, B > >, f: (a: A, b: B) => C ): HKT.Kind< F, TCF, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Kind< G, TCG, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, C > > } export interface CrossWithFn2 { < A, NF1 extends string, KF1, QF1, WF1, XF1, IF1, SF1, RF1, EF1, NG1 extends string, KG1, QG1, WG1, XG1, IG1, SG1, RG1, EG1, B, C >( fgb: HKT.Kind< F, TCF, NF1, KF1, QF1, WF1, XF1, IF1, SF1, RF1, EF1, HKT.Kind >, f: (a: A, b: B) => C ): ( fga: HKT.Kind< F, TCF, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Kind< G, TCG, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, A > > ) => HKT.Kind< F, TCF, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Kind< G, TCG, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, C > > } /* * ------------------------------------------- * Derivatives * ------------------------------------------- */ export function apF_(A: SemimonoidalFunctor): ApFn_ { return (fab, fa) => A.crossWith_(fab, fa, (f, a) => f(a)) } export function apF(A: SemimonoidalFunctor): ApFn { return (fa) => (fab) => A.crossWith_(fab, fa, (f, a) => f(a)) } export interface CrossSFn { ( name: Exclude, fb: HKT.Kind ): ( fa: HKT.Kind< F, C, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, A > ) => HKT.Kind< F, C, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, { [K in keyof A | BN]: K extends keyof A ? A[K] : A1 } > } export function crossSF(F: SemimonoidalFunctor): CrossSFn { return (name, fb) => (fa) => F.crossWith_(fa, fb, (a, b) => Object.assign({}, a, { [name]: b })) } export interface CrossTFn { (fb: HKT.Kind): < N extends string, K, Q, W, X, I, S, R, E, A extends ReadonlyArray >( fas: HKT.Kind< F, C, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, A > ) => HKT.Kind< F, C, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, readonly [...A, A1] > } export function crossTF(F: SemimonoidalFunctor): CrossTFn { return (fb) => (fas) => F.crossWith_(fas, fb, (a, b) => [...a, b]) } export interface LiftA2Fn { (f: (a: A) => (b: B) => D): < N extends string, K, Q, W, X, I, S, R, E, N1 extends string, K1, Q1, W1, X1, I1, S1, R1, E1 >( fa: HKT.Kind ) => ( fb: HKT.Kind< F, TC, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, B > ) => HKT.Kind< F, TC, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, D > } export function liftA2F(F: SemimonoidalFunctor): LiftA2Fn { return (f) => (fa) => (fb) => F.crossWith_(fa, fb, f) } export interface MapNFn { < KT extends readonly [ HKT.Kind< F, TC, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, unknown >, ...ReadonlyArray< HKT.Kind< F, TC, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, unknown > > ], B, N extends string = HKT.Initial, K = HKT.Initial, Q = HKT.Initial, W = HKT.Initial, X = HKT.Initial, I = HKT.Initial, S = HKT.Initial, R = HKT.Initial, E = HKT.Initial >( f: (...as: { [K in keyof KT]: HKT.Infer }) => B ): ( ...t: KT ) => HKT.Kind< F, TC, InferMixTuple, InferMixTuple, InferMixTuple, InferMixTuple, InferMixTuple, InferMixTuple, InferMixTuple, InferMixTuple, InferMixTuple, B > } export interface MapNFn_ { < KT extends readonly [ HKT.Kind< F, TC, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, unknown >, ...ReadonlyArray< HKT.Kind< F, TC, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, unknown > > ], N extends string = HKT.Initial, K = HKT.Initial, Q = HKT.Initial, W = HKT.Initial, X = HKT.Initial, I = HKT.Initial, S = HKT.Initial, R = HKT.Initial, E = HKT.Initial >( ...t: KT ): ( f: (...as: { [K in keyof KT]: HKT.Infer }) => B ) => HKT.Kind< F, TC, InferMixTuple, InferMixTuple, InferMixTuple, InferMixTuple, InferMixTuple, InferMixTuple, InferMixTuple, InferMixTuple, InferMixTuple, B > } /** * ```haskell * mapNF :: Apply f => ([a, b, ...] -> c) -> [f a, f b, ...] -> f c * ``` * * Combines a tuple of the given `Apply` member and maps with function `f` * * @category Apply * @since 1.0.0 */ export function mapNF(A: SemimonoidalFunctor): MapNFn export function mapNF(F: SemimonoidalFunctor>): MapNFn> { return (f) => (...t) => F.map_(sequenceTF(F)(...(t as any)), (as) => f(...(as as any))) } /** * ```haskell * mapNF_ :: Apply f => (fa, fb, ...) -> ([a, b, ...] -> c) -> f c * ``` * * Combines a tuple of the given `Apply` member and maps with function `f` * * @category Apply * @since 1.0.0 */ export function mapNF_(A: SemimonoidalFunctor): MapNFn_ export function mapNF_(F: SemimonoidalFunctor>): MapNFn_> { return (...t) => (f) => F.map_(sequenceTF(F)(...(t as any)), (as) => f(...(as as any))) } export interface SequenceSFn { < KS extends Readonly< Record< string, HKT.Kind< F, TC, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, unknown > > >, N extends string = HKT.Initial, K = HKT.Initial, Q = HKT.Initial, W = HKT.Initial, X = HKT.Initial, I = HKT.Initial, S = HKT.Initial, R = HKT.Initial, E = HKT.Initial >( r: EnforceNonEmptyRecord & Readonly< Record< string, HKT.Kind< F, TC, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, unknown > > > ): HKT.Kind< F, TC, InferMixStruct, InferMixStruct, InferMixStruct, InferMixStruct, InferMixStruct, InferMixStruct, InferMixStruct, InferMixStruct, InferMixStruct, { [K in keyof KS]: HKT.Infer } > } export function sequenceSF(F: SemimonoidalFunctor): SequenceSFn export function sequenceSF(F: SemimonoidalFunctor>): SequenceSFn> { const ap_ = apF_(F) return (r) => { const keys = Object.keys(r) const len = keys.length const f = getRecordConstructor(keys) let fr = F.map_(r[keys[0]], f) for (let i = 1; i < len; i++) { fr = ap_(fr, r[keys[i]]) as any } return fr } } export interface SequenceTFn { < KT extends readonly [ HKT.Kind< F, TC, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, unknown >, ...ReadonlyArray< HKT.Kind< F, TC, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, unknown > > ], N extends string = HKT.Initial, K = HKT.Initial, Q = HKT.Initial, W = HKT.Initial, X = HKT.Initial, I = HKT.Initial, S = HKT.Initial, R = HKT.Initial, E = HKT.Initial >( ...t: KT ): HKT.Kind< F, TC, InferMixTuple, InferMixTuple, InferMixTuple, InferMixTuple, InferMixTuple, InferMixTuple, InferMixTuple, InferMixTuple, InferMixTuple, { [K in keyof KT]: HKT.Infer } > } export function sequenceTF(F: SemimonoidalFunctor): SequenceTFn export function sequenceTF(F: SemimonoidalFunctor>): SequenceTFn> { const ap_ = apF_(F) return (...t) => { const len = t.length const f = getTupleConstructor(len) let fas = F.map_(t[0], f) for (let i = 1; i < len; i++) { fas = ap_(fas, t[i]) as any } return fas as any } } /* * ------------------------------------------- * internal * ------------------------------------------- */ /** * @internal */ function curried(f: Function, n: number, acc: ReadonlyArray) { return function (x: unknown) { const mut_combined = Array(acc.length + 1) for (let i = 0; i < acc.length; i++) { mut_combined[i] = acc[i] } mut_combined[acc.length] = x /* eslint-disable-next-line prefer-spread */ return n === 0 ? f.apply(null, mut_combined) : curried(f, n - 1, mut_combined) } } /** * @internal */ const mut_tupleConstructors: Record any> = { 1: (a) => [a], 2: (a) => (b: any) => [a, b], 3: (a) => (b: any) => (c: any) => [a, b, c], 4: (a) => (b: any) => (c: any) => (d: any) => [a, b, c, d], 5: (a) => (b: any) => (c: any) => (d: any) => (e: any) => [a, b, c, d, e] } /** * @internal */ function getTupleConstructor(len: number): (a: unknown) => any { /* eslint-disable-next-line no-prototype-builtins */ if (!mut_tupleConstructors.hasOwnProperty(len)) { mut_tupleConstructors[len] = curried(tuple, len - 1, []) } return mut_tupleConstructors[len] } /** * @internal */ function getRecordConstructor(keys: ReadonlyArray) { const len = keys.length switch (len) { case 1: return (a: any) => ({ [keys[0]]: a }) case 2: return (a: any) => (b: any) => ({ [keys[0]]: a, [keys[1]]: b }) case 3: return (a: any) => (b: any) => (c: any) => ({ [keys[0]]: a, [keys[1]]: b, [keys[2]]: c }) case 4: return (a: any) => (b: any) => (c: any) => (d: any) => ({ [keys[0]]: a, [keys[1]]: b, [keys[2]]: c, [keys[3]]: d }) case 5: return (a: any) => (b: any) => (c: any) => (d: any) => (e: any) => ({ [keys[0]]: a, [keys[1]]: b, [keys[2]]: c, [keys[3]]: d, [keys[4]]: e }) default: return curried( (...args: ReadonlyArray) => { const mut_r: Record = {} for (let i = 0; i < len; i++) { mut_r[keys[i]] = args[i] } return mut_r }, len - 1, [] ) } } /** * @internal */ type InferMixStruct = HKT.MixStruct< TC, P, T, { [K in keyof KS]: HKT.Infer } > /** * @internal */ type InferMixTuple = HKT.MixStruct< TC, P, T, { [K in keyof KT & number]: HKT.Infer } >