require("@babel/polyfill") const { Arr, Fn, Maybe: { default: Maybe } } = require("@masaeedu/fp") // Endomorphisms in any category form a monoid const Endonoid = C => ({ empty: C.id, append: C.compose }) // The reverse of a monoid is a monoid const Dual = M => ({ empty: M.empty, append: Fn.flip(M.append) }) { // Types and functions between types (i.e. a -> b) form a category const Fn_ = { // :: a -> a id: x => x, // :: (b -> c) -> (a -> b) -> (a -> c) compose: f => g => a => f(g(a)) } // Therefore, endomorphisms in this category (i.e. functions a -> a) // form a monoid const M = Endonoid(Fn) // Here is a practical use case // A list of endofunctions can be collapsed into a single endofunction // :: [a -> a] -> (a -> a) const pipe = Arr.fold(Dual(M)) // Demo const f = pipe([ x => x + 1, x => x * 2 ]) console.log(f(3)) // => 8 } { // Types and Kleisli arrows between types (i.e. a -> m b, for some monadic m) // form a category const Kleisli = M => ({ // :: a -> m a id: M.of, // :: (b -> m c) -> (a -> m b) -> (a -> m c) compose: bc => ab => Fn.compose(M.chain(bc))(ab) }) // Therefore, endomorphisms in this category (i.e. functions a -> m a, for some monadic m) // form a monoid const KM = M => Endonoid(Kleisli(M)) // Here is a practical use case // A list of endokleislis can be collapsed into a single endokleisli // :: [a -> m a] -> (a -> m a) const pipeK = M => Arr.fold(Dual(KM(M))) // Demo const f = pipeK(Maybe)([ x => Maybe.Just(x * 2), x => x > 5 ? Maybe.Nothing : Maybe.Just(x) ]) // Maybes can be displayed as strings :) const showMaybe = Maybe.match({ Nothing: "Nothing", Just: x => `Just(${x})` }) console.log(showMaybe(f(2))) // => Just(4) console.log(showMaybe(f(3))) // => Nothing }