Manu343726
5/3/2014 - 4:00 PM

Haskell-like partial function calls for C++11

Haskell-like partial function calls for C++11

/****************************************************************************
* Haskell-like automatic partial functions                                  *
*                                                                           * 
* Copyright © 2014 Manuel Sánchez Pérez                                     *
*                                                                           *
* This program is free software. It comes without any warranty, to          *
* the extent permitted by applicable law. You can redistribute it           *
* and/or modify it under the terms of the Do What The Fuck You Want         *
* To Public License, Version 2, as published by Sam Hocevar. See            *
* http://www.wtfpl.net/ for more details.                                   *
****************************************************************************/

#include <functional>
#include <tuple>
#include <iostream>
#include <cassert>

/*
 * The Haskell programming language returns partial functions automatically when a function is evaluated with not enough argumments.
 * For a function f with N argumments, an evaluation with M parameters returns a function with N - M parameters if N > M. If N = M the result
 * of the evaluation is the call to the function.
 * 
 * This snippet tries to mimic that behaviour providing a type, haskell::function, which automatically generates another partial function if 
 * the original function is called with less argumments than required. 
 */


/* Indices trick */

namespace tuple_call_impl
{
    template<std::size_t...>
    struct sequence
    {};

    template<std::size_t N , std::size_t... S>
    struct sequence_generator : sequence_generator<N-1,N-1,S...>
    {};

    template<std::size_t... S>
    struct sequence_generator<0,S...>
    {
        using result = sequence<S...>;
    };

    template<std::size_t N>
    using generate_sequence = typename sequence_generator<N>::result;


    template<typename F , typename T , std::size_t... SEQ>
    auto tuple_call( F function , const T& tuple , sequence<SEQ...> )
    {
        return function( std::get<SEQ>( tuple )... );
    }
}


//Calls a function with the elements of a tuple as parameters (There is a proposal for a similar std::call(), isn't?)

template<typename F , typename... Ts>
auto tuple_call( F function , const std::tuple<Ts...>& tuple )
{
    return tuple_call_impl::tuple_call( function , tuple , tuple_call_impl::generate_sequence<sizeof...(Ts)>{} );
}

/*
 * Haskell-like function type. Automatically generates partial functions from partial calls (See examples in main() )
 * Supports a Haskell-like calling syntax based in commas (A call to f with three params: f , 1 , 2 , 3 ) and the common C++ parenthesis
 * based syntax (A call to f with three params: f( 1 , 2 , 3 ) )
 */
namespace haskell
{
    template<typename S , typename ARGS = std::tuple<>>
    struct function
    {
    public:
        template<typename F>
        function( F f , const ARGS& args ) : 
            _function{ f },
            _args{ args }
        {}
            
        template<typename F>
        function( F f ) : 
            _function{ f }
        {}

        template<typename ARG>
        auto operator,(ARG&& arg ) const
        {
            auto args_tuple = std::tuple_cat( _args , std::make_tuple( arg ) );

            return call( args_tuple );
        }
        
        template<typename HEAD>
        auto operator()( HEAD&& head ) const
        {
            return ((*this) , head);
        }
        
        template<typename HEAD  , typename... TAIL , typename = typename std::enable_if<(sizeof...(TAIL) > 0)>::type>
        auto operator()( HEAD&& head , TAIL&&... tail) const
        {
            return ((*this) , head)(tail...);
        }

    private:
        template<typename T>
        struct deduce_params;

        template<typename R , typename... FARGS>
        struct deduce_params<R(FARGS...)> 
        {
            using result = std::tuple<FARGS...>;
        };

        template<typename T>
        struct deduce_return;

        template<typename R , typename... FARGS>
        struct deduce_return<R(FARGS...)> 
        {
            using result = R;
        };

        using return_type = typename deduce_return<S>::result;
        using params_type = typename deduce_params<S>::result;

        template<typename PARAMS_TUPLE , typename PARS = params_type,
                 typename = typename std::enable_if<std::is_same<PARAMS_TUPLE,PARS>::value>::type>
        return_type call( const PARAMS_TUPLE& params ) const
        {
            return tuple_call( _function , params );
        }

        template<typename PARAMS_TUPLE , typename PARS = params_type,
                 typename = typename std::enable_if<!std::is_same<PARAMS_TUPLE,PARS>::value>::type>
        function<S,PARAMS_TUPLE> call( const PARAMS_TUPLE& params ) const
        {
            return function<S,PARAMS_TUPLE>{ _function , params };
        }


        std::function<S> _function;
        ARGS _args;
    };
    
   /*
    * function() Should work with regular functions and functors (Like lambdas).
    * The contents of this namespace extracts the signature of a functor, 
    * and resolve the calls casting the functor into the proper C function pointer type.
    */
   namespace function_impl
   {
       //Overload for function pointers
       template<typename R , typename... ARGS>
       auto function( R(*f)(ARGS...) )
       {
           return haskell::function<R(ARGS...)>{ f };
       }


       template<typename T>
       struct deduce_type;

       //Non-const operator():
       template<typename C , typename R , typename... ARGS>
       struct deduce_type<R(C::*)(ARGS...)> 
       {
           using result = R(*)(ARGS...);
       };

       //Const operator()
       template<typename C , typename R , typename... ARGS>
       struct deduce_type<R(C::*)(ARGS...) const>
       {
           using result = R(*)(ARGS...);
       };

       //Overload for functors:
       template<typename F>
       auto function( F f )
       {
           using fpointer_type = typename deduce_type<decltype(&F::operator())>::result;

           return function( static_cast<fpointer_type>( f ) );
       }
   }
   
   //A factory of Haskell-like functions: Constructs a Haskell-like function from any kind of C++ function entity
   template<typename F>
   auto make_function( F f )
   {
       return function_impl::function( f );
   }
}






//Ohh, the magic of the C preprocessor...
#define function( ... ) haskell::make_function([](__VA_ARGS__)

/*
 * This is a definition of a function called f. The intention was to create a natural syntax to declare functions.
 * Something like:
 * 
 *     f = function( args... )
 *     {
 * 
 *     }
 * 
 * Personally, this syntax reminds me to Javascript functions...
 */

auto f = function( int a , int b , int c , int d , int e , int f )
{
    return 42; //The answer to the function call is...
});


int main()
{
    //Haskell-like syntax
    auto g1 = (f , 1 , 2);
    auto h1 = (g1 , 3 , 4 , 5);
    auto result1 = (h1 , 6);
    
    std::cout << result1 << std::endl;
    
    //C++ syntax
    auto g2 = f(1 , 2);
    auto h2 = g2(3 , 4, 5);
    auto result2 = h2(6);
    
    std::cout << result2 << std::endl;
}