Endomorphisms in a category form a monoid
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
}
no comments