#include <functional>
#include <utility>
#include <vector>
#include <algorithm>
#include <iostream>

template<template<class ...> class M>
struct Monad {
    // return :: a -> m a
    template<typename T>
    static auto ret(T a) -> M<T>;
 
    // bind :: m a -> (a -> m b) -> m b
    template<typename T, typename U>
    static auto bind(M<T>, std::function<M<U>(T)>) -> M<U>;
};
 
// return :: a -> m a
template<template<class...> class M, typename T>
auto ret(T a) -> M<T>
{
    return Monad<M>::ret(std::move(a));
}
 
// (>>=) :: m a -> (a -> m b) -> m b
template<template<class...> class M, typename T, typename F>
auto operator >>= (M<T> ma, F fun) -> decltype(fun(std::declval<T>()))  // запутанно, но не знаю, как сделать лучше, чтобы работали лямбды
{
    using R = decltype(fun(std::declval<T>()));
    return Monad<M>::bind(ma, std::function<R(T)>(std::move(fun)));
}
 
// (>>) :: m a -> m b -> m b
template<template<class...> class M, typename T, typename U>
auto operator >> (M<T> ma, M<U> mb) -> M<U>
{
    return ma >>= [=](T){ return mb; };
}

template<typename T>
struct list : private std::vector<T> {
	using impl = std::vector<T>;
	using impl::value_type;

	list() = default;
	list(std::initializer_list<T> il)
		: std::vector<T>(std::move(il))
    {
	}

	using impl::push_back;
	using impl::insert;
	using impl::begin;
	using impl::end;
};

template<typename T>
auto mk_list(std::initializer_list<T> il) -> list<T>
{
	return list<T>(il);
}

template<>
template<typename T>
auto Monad<list>::ret(T a) -> list<T>
{
	// заворачиваем объект в список
	return list<T>{a};
}

template<>
template<typename T, typename U>
auto Monad<list>::bind(list<T> ma, std::function<list<U>(T)> f) -> list<U>
{
	// формируем список списков путем применения к каждому элементу переданной функции
	// (которая принимает объект, а возвращает монаду(список))
	list<list<U> > tmp;
	std::transform(ma.begin(), ma.end(), std::back_inserter(tmp), f);

	// конкатенируем списки в один
	list<U> res;
	for (auto & v : tmp) {
		res.insert(res.end(), v.begin(), v.end());
	}
	return res;
}

template<typename T, typename U>
std::ostream & operator << (std::ostream & os, std::pair<T,U> p)
{
	os << "(" << p.first << "," << p.second << ")";
	return os;
}
 
template<typename T>
std::ostream & operator << (std::ostream & os, const list<T> & a)
{
	os << "[";
	int i = 0;
	for (auto & e : a) {
		if (i++) os << ",";
		os << e;
	}
	os << "]";
	return os;
}

int main()
{
	auto res = mk_list({1, 2, 3}) >>= [](auto x) {
		return mk_list({4, 5, 6}) >>= [=](auto y) {
			return ret<list>(std::make_pair(x, y));
		};
	};
	std::cout << res << std::endl;
}
