// Hide the allocator template argument of std::vector. // It causes problems and is irrelevant here. template <class T> struct Vector : std::vector<T> {}; struct Continent { }; struct Country { }; struct State { }; struct City { }; auto get_countries = [](Continent& c) -> Vector<Country> { return ... } ; auto get_states = [](Country& c) -> Vector<State> { return ... }; auto get_cities = [](State& s) -> Vector<City> { return ... };

Continent c; auto cities_from_continent = composeM(get_countries, get_states, get_cities); Vector<City> cities = cities_from_continent(c);

template <class... Args> auto composeM(Args&&... args) { return (... >>= args); } template <class F, class G> auto operator >>= (F&& f, G&& g) { using FArg = arg_type_t<F>; using GResult = result_type_t<G>; return [f,g] (FArg a) -> GResult { using FResult = result_type_t<F>; GResult finalvec; FResult const & fvec = f(a); for(const auto& o: fvec) { const GResult& gvec = g(o); finalvec.insert(finalvec.end(), gvec.begin(), gvec.end()); } return finalvec; }; }

Get a Continent from somewhere. Initialize an empty vector of cities. Call get_countries (level 1) Call get_states for each country (level 2) Call get_cities for each state (level 3) and push_back in the vector of cities Return the vector of cities

Another Example: boost::future

auto get_largest_country = [](Continent const &) -> boost::future<Country> { ... }; auto get_largest_state = [](Country const &) -> boost::future<State> { ... }; auto get_largest_city = [](State const &) -> boost::future<City> { ... }

Get a Continent from somewhere. Call get_largest_country (level 1). Wait to retrieve the result because db ops could take long. Call get_largest_state for that country (level 2). Wait again. Call get_largest_city for that state (level 3). Wait again. Return the city

auto get_largest_city_from_continent = [](Continent const &c) { return get_largest_city(get_largest_state(get_largest_country(c).get()).get()).get(); };

template <class F, class G> auto operator >>= (F&& f, G&& g) { using FArg = arg_type_t<F>; using GResult = result_type_t<G>; return [f,g] (FArg a) -> GResult { return g(f(a).get()); }; }

Refactoring To a Generic operator >>=

Both functions are higher-order. I.e. they take functions as arguments and return a new composite function. The type of the composite function depends on the types of the arguments. Specifically, the composite function is "just like" the argument functions. In both cases, the argument type of the composite function is the same as that of F and the return type is the same as that of G. Function f and g accept an argument of a simple type and return a value "wrapped" in a generic container. The notable difference is of the container type (Vector and boost:future, respectively). Hmm, template template parameter? There's a dependency between f and g. f is invoked before g. Always. That's generic. This is key. The argument passed to the composite function is passed to f (i.e., f(a)). As the return value of f is a generic container, the contained value(s) are somehow extracted and passed to g. In the case of Vector we use a for loop and in the case of boost::future, we use .get(). The specific way to get to the guts of the container is dependent on the container type. The composite function "collapses" two nested levels into one. In the case of Vector, two nested loops. In the case of boost::future, two calls to the database appear as one from outside. These things are very very specific to the container. There's no hope to generalize it. This is key too and the most hand-wavy observation.

The Genius of Monad

template <class F, class G> auto operator >>= (F&& f, G&& g) { using FArg = arg_type_t<F>; using GResult = result_type_t<G>; return [f,g] (FArg a) -> GResult { return get_monad<GResult>::bind(f(a), std::move(g)); }; }

template <class T> struct get_monad; template <template <typename> class M, class T> struct get_monad<M<T>> : monad<M> {};

template<template <typename> class M> struct monad { template <class T, class Func> static auto bind(M<T>& m, Func&& func) -> result_type_t<Func>; };

template<> struct monad<Vector> { template <class T, class Func> static auto bind(const Vector<T>& vec, Func&& func) -> result_type_t<Func> { // Alternatively, // using Outer = decltype(func(std::declval<T>())); using Outer = result_type_t<Func>; Outer outer; for(const T& o: vec) { const Outer& inner = func(o); outer.insert(outer.end(), inner.begin(), inner.end()); } return outer; } };

template<> struct monad<boost::future> { template <class T, class Func> static auto bind(boost::future<T> fut, Func&& func) -> result_type_t<Func> { return func(fut.get()); } };

#define BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION #define BOOST_THREAD_PROVIDES_FUTURE_UNWRAP #define BOOST_THREAD_PROVIDES_FUTURE #include <boost/thread/future.hpp> template<> struct monad<boost::future> { template <class T, class Func> static auto bind(boost::future<T> fut, Func&& func) -> result_type_t<Func> { return fut.then([func](boost::future<T> f) mutable { return func(f.get()); // calling .get here does not block. }).unwrap(); } };

Putting It All Together

void test() { auto get_cities_from_continent = composeM(get_countries, get_states, get_cities); auto get_largest_city_from_continent = composeM(get_largest_country, get_largest_state, get_largest_city); Continent c; auto cities = get_cities_from_continent(c); auto largest_city = get_largest_city_from_continent(c).get(); std::cout << cities << "

" << largest_city << "

"; }

Something's Amiss?

template<> struct monad<Vector> { template <class T> static Vector<std::decay_t<T>> return_(T&& t) { Vector<std::decay_t<T>> vec; vec.push_back(std::forward<T>(t)); return vec; // return { t }; // gcc hates this line. } .... }; template<> struct monad<boost::future> { template <class T> static boost::future<std::decay_t<T>> return_(T&& t) { return boost::make_ready_future(std::forward<T>(t)); } .... };

template <class Head, class... Tail> struct head { using type = Head; }; template <class... Args> auto composeM(Args&&... args) { static_assert(sizeof...(Args) > 0); using Func = typename head<Args...>::type; using FArg = arg_type_t<Func>; auto identity = [](FArg a) { return get_monad<result_type_t<Func>>::return_(a); }; return (identity >>= ... >>= args); }

template <class F, class G> auto operator >>= (F&& f, G&& g) { //using FArg = arg_type_t<F>; not needed using GResult = result_type_t<G>; return [f=std::forward<F>(f), g=std::forward<G>(g)] (auto&& a) mutable -> GResult { return get_monad<GResult>::bind(f(std::forward<decltype(a)>(a)), std::forward<G>(g)); }; }

Appendix

namespace detail { template <typename Func> struct function_traits { using arg_type = typename function_traits<decltype(&Func::operator())>::arg_type; using result_type = typename function_traits<decltype(&Func::operator())>::result_type; }; template <typename Func> struct function_traits<Func &> { using arg_type = typename function_traits<decltype(&Func::operator())>::arg_type; using result_type = typename function_traits<decltype(&Func::operator())>::result_type; }; template <typename R, typename C, typename A> struct function_traits<R (C::*)(A)> { using result_type = R; using arg_type = A; }; template <typename R, typename C, typename A> struct function_traits<R (C::*)(A) const> { using result_type = R; using arg_type = A; }; template <typename R, typename A> struct function_traits<R (*)(A)> { using result_type = R; using arg_type = A; }; template <class T> using result_type_t = typename function_traits<T>::result_type; template <class T> using arg_type_t = typename function_traits<T>::arg_type; }