Manu343726
8/23/2014 - 5:38 PM

C++14 tuple continuation monad

C++14 tuple continuation monad

//Author: Manu Sánchez (Manu343726). Aug 2014
 
//Everything started from this: http://stackoverflow.com/questions/25338795/is-there-a-name-for-this-tuple-creation-idiom
//Now, play with C++14!
 
#include <utility>
#include <iostream>
#include <exception>

template<typename... Ls>
struct lambdas_ : public Ls...
{
    lambdas_( const Ls&... ls ) :
        Ls( ls )...
    {}

    lambdas_(Ls&&... ls ) : Ls( ls )...
    {}
};

template<typename... Ls>
lambdas_<typename std::decay<Ls>::type...> lambdas( Ls&&... ls )
{
    return { std::forward<Ls>( ls )... };
}

template<typename D>
struct tuple_erase_base
{
    template<typename... ARGS>
    auto operator()(ARGS&&... args)
    {
        return static_cast<D*>(this)->tuple(std::forward<ARGS>( args )...);
    }

    tuple_erase_base& as_base()
    {
        return *this;
    }
};

template<typename T>
struct tuple_erase : public tuple_erase_base<tuple_erase<T>>
{
    friend class tuple_erase_base<tuple_erase<T>>;

    tuple_erase( T tuple_ ) : tuple( std::move( tuple_ ) )
    {}

private:
    T tuple;
};


template<typename T>
tuple_erase<typename std::decay<T>::type> erase( T&& tuple )
{
    return { std::forward<T>( tuple ) };
}



auto fix_rec = [](auto f)
{
    return [=](auto... args)
    {
        return f(f,args...);   
    }; 
};



auto tuple = [](auto... args)
{
    return erase([=](auto f){ return f(args...); });  
};

auto as_base = [](auto... args)
{
    return tuple(args...).as_base();
}

template<typename HEAD , typename... TAIL>
struct last_type
{
    using type = typename last_type<TAIL...>::type;   
};

template<typename T>
struct last_type<T>
{
    using type = T;
};

auto ituple = [](auto... args)
{
    return [=](std::size_t i)
    {   
        return fix_rec(lambdas( 
        [](auto self , std::size_t j , auto head , auto... tail)
        {
            if( j == 0 )
                return head;
            else
                return [=]() -> decltype(head) { return self(self , j - 1 , tail...); }();     
        },
        [](auto self , std::size_t j) -> typename last_type<decltype(args)...>::type 
        { 
            throw std::out_of_range{"Index out of bounds"}; 
        }
        ))(i,args...);
    };
};

auto at = ituple;


auto identity = [](auto x)
{
    return x;
};
 
auto map = [](auto... args)
{
    return [=](auto f){ return tuple(f(args)...); };  
};

auto foldl_ = [](auto self , auto... args)
{
    return [=](auto f , auto state) -> decltype(state)
    {
        auto rec = [=](auto arg , auto... tail) -> decltype(state)
        {
            return self(self,tail...)(f,f(state,arg));
        };

        auto base = [=]()
        {
            return state;
        }; 

        return lambdas(rec,base)(args...);
    };
};

auto foldl = [](auto... args)
{
    return foldl_(foldl_,args...);
};
 
auto cat = []( auto... largs )
{
    auto closure_ = [=](auto... rargs ) { return tuple(largs...,rargs...); };
    
    return [=](auto rhs)
    {
        return rhs(closure_);
    };
};

auto filter = []( auto... args )
{
    return [=](auto f)
    {
        auto base = [](auto self , auto passed){ return passed; };
        auto rec  = [f](auto self , auto passed , auto head , auto... tail )
        {
            auto next = f(head) ? passed(cat)(tuple(head,tail...)) : passed(cat)(tuple(tail...));   
            
            return self(self, next , tail...);
        };

        return fix_rec(lambdas(base,rec))(tuple(),args...);
    };
};

auto size = [](auto... args)
{
    return sizeof...(args);   
};
 
auto print_tuple = [](auto t , std::ostream& os = std::cout){ return t(map)( [&](auto e){ os << e << " "; return e; } ); };

int main()
{
    auto t = tuple(1,2,3,4,5)(cat)(tuple(6,7,8,9))
             (map)([](auto i){ return i*2; })
             (map)([](auto i){ return i - 1; })
             (foldl)( [](auto s , auto i){ return s + i;} , 0 );
    
    std::cout << t << std::endl;

    print_tuple( true ? tuple(1)(cat)(tuple(2))(as_base) : tuple()(cat)(tuple(1,2,3,4))(as_base);
}