// @flow import type { Functor } from './Functor' import type { Monad } from './Monad' import type { HKT2 } from './HKT' import { HKT } from './HKT' import { Data1 } from './Data' class IsFree {} // we can think of a free monad as just being a list of functors class Suspend extends Data1>> {} class Return extends Data1 {} export type FreeV = Return | Suspend; export type Free = HKT2; export function inj(f: FreeV): Free { return ((f: any): Free) } export function prj(fa: Free): FreeV { return ((fa: any): FreeV) } export function of(a: A): Free { return inj(new Return(a)) } export function suspend(ffa: HKT>): Free { return inj(new Suspend(ffa)) } export function liftFree(functor: Functor, fa: HKT): Free { return suspend(functor.map(of, fa)) } export function foldFree(functor: Functor, join: (fa: HKT) => A, ffa: Free): A { const fa = prj(ffa) if (fa instanceof Return) { return fa.value0 } return join(functor.map(x => foldFree(functor, join, x), fa.value0)) } export function freeMonad(functor: Functor): Monad> { function map(f: (a: A) => B, fa: Free): Free { const a = prj(fa) if (a instanceof Return) { return of(f(a.value0)) } return suspend(functor.map(x => map(f, x), a.value0)) } function ap(fab: Free B>, fa: Free): Free { return chain(f => map(f, fa), fab) // <= derived } function join(ffa: Free>): Free { const fa = prj(ffa) if (fa instanceof Return) { return fa.value0 } return suspend(functor.map(join, fa.value0)) } function chain(f: (a: A) => Free, fa: Free): Free { return join(map(f, fa)) // <= derived } return { map, ap, of, chain } }