import type { FunctorMin } from './Functor' import { identity } from './function' import { Functor } from './Functor' import * as HKT from './HKT' export interface Bind extends Functor { readonly bind_: BindFn_ readonly bind: BindFn readonly flatten: FlattenFn readonly tap_: TapFn_ readonly tap: TapFn } export type BindMin = FunctorMin & ({ readonly bind_: BindFn_ } | { readonly flatten: FlattenFn }) export function Bind(F: BindMin): Bind { const FunctorF = Functor(F) let bind_: BindFn_ let flatten: FlattenFn if ('bind_' in F) { bind_ = F.bind_ flatten = (mma) => F.bind_(mma, identity) } else { bind_ = (ma, f) => F.flatten(F.map_(ma, f)) flatten = F.flatten } const tap_: TapFn_ = (ma, f) => bind_(ma, (a) => F.map_(f(a), () => a)) return HKT.instance>({ ...FunctorF, bind_, bind: (f) => (ma) => bind_(ma, f), flatten, tap_, tap: (f) => (ma) => tap_(ma, f) }) } export interface BindFn { ( f: (a: A) => HKT.Kind ): ( ma: 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, B > } export interface BindFn_ { ( ma: HKT.Kind, f: ( a: A ) => 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, B > } export function bindF_(F: BindMin): BindFn_ { if ('bind_' in F) { return F.bind_ } else { return (ma, f) => F.flatten(F.map_(ma, f)) } } export interface BindFnComposition { < NF1 extends string, KF1, QF1, WF1, XF1, IF1, SF1, RF1, EF1, NG1 extends string, KG1, QG1, WG1, XG1, IG1, SG1, RG1, EG1, A, B >( f: ( a: A ) => HKT.Kind< F, TCF, NF1, KF1, QF1, WF1, XF1, IF1, SF1, RF1, EF1, HKT.Kind > ): ( 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, B > > } export interface BindFnComposition_ { < 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 >( fga: HKT.Kind>, f: ( a: A ) => 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 > > ): 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, B > > } export interface TapFn { ( f: (a: A) => HKT.Kind ): ( ma: 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, A > } export interface TapFn_ { ( ma: HKT.Kind< F, C, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, HKT.Intro, A >, f: (a: A) => HKT.Kind ): HKT.Kind< F, C, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, HKT.Mix, A > } export function tapF(F: BindMin): TapFn { const bind_ = bindF_(F) return (f) => (ma) => bind_(ma, (a) => F.map_(f(a), () => a)) } export function tapF_(F: BindMin): TapFn_ { const bind_ = bindF_(F) return (ma, f) => bind_(ma, (a) => F.map_(f(a), () => a)) } export interface FlattenFn { ( mma: HKT.Kind< F, TC, N2, K2, Q2, W2, X2, I2, S2, R2, E2, 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, A > } export interface FlattenFnComposition { < 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 >( fgfga: HKT.Kind< F, TCF, NF, KF, QF, WF, XF, IF, SF, RF, EF, HKT.Kind< G, TCG, NG, KG, QG, WG, XG, IG, SG, RG, EG, 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, A > > } export function flattenF(M: BindMin): FlattenFn { if ('flatten' in M) { return M.flatten } else { return (mma) => M.bind_(mma, identity) } }