Skip to content

Instantly share code, notes, and snippets.

@Manu343726
Created May 3, 2014 16:00
Show Gist options
  • Save Manu343726/94769034179e2c846acc to your computer and use it in GitHub Desktop.
Save Manu343726/94769034179e2c846acc to your computer and use it in GitHub Desktop.
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;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment