Monáda (funkcionální programování)

Monáda je funktoriální datový typ vybavený dvěma přirozenými transformacemi umožňujícími asociativní skládání operací nad monádami. Formálně je monáda monoidem v kategorii endofunktorů v kategorii typů (v Haskellu kategorie Hask). Obecně je-li tenzorová kategorie (kde je bifunktor a ), monády jsou monoidy v .

Přirozené transformace jsou u každé monády multiplikace a jednotka splňující a . Ve funkcionálním programování se tyto operace zpravidla nazývají join a unit. Příkladem monády je kontinuace, kde je funkcí z do definovaná jako a speciální operace vázání je , přičemž je .

Monády umožňují formulovat kód s vedlejšími efekty tak, aby byl referenčně transparentní, a tedy „čistý“ (ve funkcionálním smyslu). Monády jsou zpravidla generické a jejich druh je .

Z každého endofunktoru lze vytvořit volnou monádu.

Příklad

Kontinuace v C++ lze reprezentovat (s využitím statického polymorfismu) následující třídou:

template<typename T> class Cont {
    std::function<std::any(const std::function<std::any(T)>&)> fn;
public:
    Cont(const decltype(fn)& f) : fn(f) {}
    Cont(T&& x) : fn([x](auto f) { return f(x); }) {}
    std::any operator()(const std::function<std::any(T)>& f) const { return fn(f); }
};

Aby byla Cont funktorem, musíme implementovat fmap nebo bind:

template<template<typename> class M, typename T, typename U> struct Bind;
template<typename T, typename U> struct Bind<Cont,T,U> {
    std::function<Cont<U>(T)> f;
    auto operator()(const Cont<T>& m) {
        return [this,m](auto g) {
            return m([this,g](auto x) { return f(x)(g); });
        };
    }
};

C++ nemá typy vyšších druhů, nicméně pomocí specializace šablon můžeme dosáhnout stejného efektu. Konkrétně můžeme například definovat generické monadické operace fmap a join.

template<template<typename> class M, typename T, typename U> struct Fmap {
    std::function<U(T)> f;
    auto operator()(const M<T>&) const;
};

template<template<typename> class M, typename T> struct Join {
    auto operator()(const M<M<T>>&) const;
};

template<template<typename> class M, typename T, typename U> auto Fmap<M,T,U>::operator()(const M<T>& m) const {
    std::cerr << "generic fmap" << std::endl;
    return Bind<M,T,U>{[&](auto x) { return M<U>{f(x)}; }}(m);
}

template<template<typename> class M, typename T> auto Join<M,T>::operator()(const M<M<T>>& m) const {
    std::cerr << "generic join" << std::endl;
    return Bind<M,M<T>,T>{[](M<T> x) { return x; }}(m);
}

Takto koncipovaný kód má tu výhodu, že poskytuje definice monadických funkcí bez ohledu na konkrétní typ. Proto stačí pro konkrétní monadický typ implementovat pouze buď bind, nebo fmap a join, podobně jako v čistě funkcionálních jazycích à la Haskell.

Externí odkazy