How can currying be done in C++?
Asked Answered
M

12

64

What is currying?

How can currying be done in C++?

Please Explain binders in STL container?

Moke answered 30/9, 2008 at 6:51 Comment(0)
S
41

In short, currying takes a function f(x, y) and given a fixed Y, gives a new function g(x) where

g(x) == f(x, Y)

This new function may be called in situations where only one argument is supplied, and passes the call on to the original f function with the fixed Y argument.

The binders in the STL allow you to do this for C++ functions. For example:

#include <functional>
#include <iostream>
#include <vector>

using namespace std;

// declare a binary function object
class adder: public binary_function<int, int, int> {
public:
    int operator()(int x, int y) const
    {
        return x + y;
    }
};

int main()
{
    // initialise some sample data
    vector<int> a, b;
    a.push_back(1);
    a.push_back(2);
    a.push_back(3);

    // here we declare a function object f and try it out
    adder f;
    cout << "f(2, 3) = " << f(2, 3) << endl;

    // transform() expects a function with one argument, so we use
    // bind2nd to make a new function based on f, that takes one
    // argument and adds 5 to it
    transform(a.begin(), a.end(), back_inserter(b), bind2nd(f, 5));

    // output b to see what we got
    cout << "b = [" << endl;
    for (vector<int>::iterator i = b.begin(); i != b.end(); ++i) {
        cout << "  " << *i << endl;
    }
    cout << "]" << endl;

    return 0;
}
Steakhouse answered 30/9, 2008 at 6:59 Comment(5)
Marcin: raj requested the C++ example before I added it. :)Steakhouse
I wish I could vote up a comment trail. This comment chain is amusing. :)Evacuation
Actually, I think this answer is wrong; what this answer describes is "partial application", which the bind functions do. "Currying" is the process of transforming a function taking N arguments into a function taking one argument and returning a function with N-1 arguments, e.g. void (*)(int, short, bool) becomes X (*)(int) with X being void (*)(short, bool). See haskell.org/haskellwiki/Currying for all you ever wanted to know about currying and partial function application.Ozone
@FrerichRaabe You are strictly correct; but the point of currying is to achieve (easy) partial application.Recurvate
@Marcin: I've come to find this question because I want currying and not just any partial application. The difference is that when you have function with 6 arguments carefully written so that you need to fix the first argument, you don't want to put all the placeholders in there!Landholder
T
85

1. What is currying?

Currying simply means a transformation of a function of several arguments to a function of a single argument. This is most easily illustrated using an example:

Take a function f that accepts three arguments:

int
f(int a,std::string b,float c)
{
    // do something with a, b, and c
    return 0;
}

If we want to call f, we have to provide all of its arguments f(1,"some string",19.7f).

Then a curried version of f, let's call it curried_f=curry(f) only expects a single argument, that corresponds to the first argument of f, namely the argument a. Additionally, f(1,"some string",19.7f) can also be written using the curried version as curried_f(1)("some string")(19.7f). The return value of curried_f(1) on the other hand is just another function, that handles the next argument of f. In the end, we end up with a function or callable curried_f that fulfills the following equality:

curried_f(first_arg)(second_arg)...(last_arg) == f(first_arg,second_arg,...,last_arg).

2. How can currying be achieved in C++?

The following is a little bit more complicated, but works very well for me (using c++11)... It also allows currying of arbitrary degree like so: auto curried=curry(f)(arg1)(arg2)(arg3) and later auto result=curried(arg4)(arg5). Here it goes:

#include <functional>

namespace _dtl {

    template <typename FUNCTION> struct
    _curry;

    // specialization for functions with a single argument
    template <typename R,typename T> struct
    _curry<std::function<R(T)>> {
        using
        type = std::function<R(T)>;
        
        const type
        result;
        
        _curry(type fun) : result(fun) {}
        
    };

    // recursive specialization for functions with more arguments
    template <typename R,typename T,typename...Ts> struct
    _curry<std::function<R(T,Ts...)>> {
        using
        remaining_type = typename _curry<std::function<R(Ts...)> >::type;
        
        using
        type = std::function<remaining_type(T)>;
        
        const type
        result;
        
        _curry(std::function<R(T,Ts...)> fun)
        : result (
            [=](const T& t) {
                return _curry<std::function<R(Ts...)>>(
                    [=](const Ts&...ts){ 
                        return fun(t, ts...); 
                    }
                ).result;
            }
        ) {}
    };
}

template <typename R,typename...Ts> auto
curry(const std::function<R(Ts...)> fun)
-> typename _dtl::_curry<std::function<R(Ts...)>>::type
{
    return _dtl::_curry<std::function<R(Ts...)>>(fun).result;
}

template <typename R,typename...Ts> auto
curry(R(* const fun)(Ts...))
-> typename _dtl::_curry<std::function<R(Ts...)>>::type
{
    return _dtl::_curry<std::function<R(Ts...)>>(fun).result;
}

#include <iostream>

void 
f(std::string a,std::string b,std::string c)
{
    std::cout << a << b << c;
}

int 
main() {
    curry(f)("Hello ")("functional ")("world!");
    return 0;
}

View output

OK, as Samer commented, I should add some explanations as to how this works. The actual implementation is done in the _dtl::_curry, while the template functions curry are only convenience wrappers. The implementation is recursive over the arguments of the std::function template argument FUNCTION.

For a function with only a single argument, the result is identical to the original function.

        _curry(std::function<R(T,Ts...)> fun)
        : result (
            [=](const T& t) {
                return _curry<std::function<R(Ts...)>>(
                    [=](const Ts&...ts){ 
                        return fun(t, ts...); 
                    }
                ).result;
            }
        ) {}

Here the tricky thing: For a function with more arguments, we return a lambda whose argument is bound to the first argument to the call to fun. Finally, the remaining currying for the remaining N-1 arguments is delegated to the implementation of _curry<Ts...> with one less template argument.

Update for c++14 / 17:

A new idea to approach the problem of currying just came to me... With the introduction of if constexpr into c++17 (and with the help of void_t to determine if a function is fully curried), things seem to get a lot easier:

template< class, class = std::void_t<> > struct 
needs_unapply : std::true_type { };
 
template< class T > struct 
needs_unapply<T, std::void_t<decltype(std::declval<T>()())>> : std::false_type { };

template <typename F> auto
curry(F&& f) {
  /// Check if f() is a valid function call. If not we need 
  /// to curry at least one argument:
  if constexpr (needs_unapply<decltype(f)>::value) {
       return [=](auto&& x) {
            return curry(
                [=](auto&&...xs) -> decltype(f(x,xs...)) {
                    return f(x,xs...);
                }
            );
        };    
  }
  else {  
    /// If 'f()' is a valid call, just call it, we are done.
    return f();
  }
}

int 
main()
{
  auto f = [](auto a, auto b, auto c, auto d) {
    return a  * b * c * d;
  };
  
  return curry(f)(1)(2)(3)(4);
}

See code in action on here. With a similar approach, here is how to curry functions with arbitrary number of arguments.

The same idea seems to work out also in C++14, if we exchange the constexpr if with a template selection depending on the test needs_unapply<decltype(f)>::value:

template <typename F> auto
curry(F&& f);

template <bool> struct
curry_on;

template <> struct
curry_on<false> {
    template <typename F> static auto
    apply(F&& f) {
        return f();
    }
};

template <> struct
curry_on<true> {
    template <typename F> static auto 
    apply(F&& f) {
        return [=](auto&& x) {
            return curry(
                [=](auto&&...xs) -> decltype(f(x,xs...)) {
                    return f(x,xs...);
                }
            );
        };
    }
};

template <typename F> auto
curry(F&& f) {
    return curry_on<needs_unapply<decltype(f)>::value>::template apply(f);
}

If you want to force our curry function to inline, we can apply compiler specific macros defined like this.

#if defined(_MSC_VER)
#define FORCE_INLINE __forceinline
#elif defined(__GNUC__) || defined(__GNUG__)
#define FORCE_INLINE __attribute__((always_inline))
#else // Clang or else
#define FORCE_INLINE inline
#endif
Tchad answered 5/11, 2014 at 22:29 Comment(12)
you need to explain in wording the answer not just post a code, the OP was not just asking for a code, he was asking for explanation!Neall
+1 This is the first answer that actually provides a solution to "How is currying done in C++?". As pointed out pointed out in the comments of some other answers, general currying (as implemented here) is different from partial application of a finite number of specific arguments.Greyhound
check also Luc Danton's answer in Partial application with a C++ lambda? which provides also a nice and maybe somewhat more complete implementation on the subject of partial application.Tchad
This is the answer I am looking for: implementing currying with variadic function templates.Hurtful
Don't use double underscores anywhere in your C++ code (unless you are a compiler vendor). It's simply not legal.Malebranche
@ThomasEding Isn't it legal as long as you don't start with double underscores?Targett
@ThomasEding: Thx for the comment! I actually didn't know that, since it compiled just fine...Tchad
@Poldie: You cannot start an identifier with an underscore followed by a capital letter.Malebranche
@ThomasEding Or a numeric digit.Targett
std::void_t is also C++17 but can be replaced: en.cppreference.com/w/cpp/types/void_tPrognostic
Without any explanation, this is not helpful at all.Ganley
I would improve upon c++17 solution by using std::is_invocable_v instead of needs_unapply: ... if constexpr (std::is_invocable_v<F>) ... godbolt.org/z/cMcMY5GooSabbatical
S
41

In short, currying takes a function f(x, y) and given a fixed Y, gives a new function g(x) where

g(x) == f(x, Y)

This new function may be called in situations where only one argument is supplied, and passes the call on to the original f function with the fixed Y argument.

The binders in the STL allow you to do this for C++ functions. For example:

#include <functional>
#include <iostream>
#include <vector>

using namespace std;

// declare a binary function object
class adder: public binary_function<int, int, int> {
public:
    int operator()(int x, int y) const
    {
        return x + y;
    }
};

int main()
{
    // initialise some sample data
    vector<int> a, b;
    a.push_back(1);
    a.push_back(2);
    a.push_back(3);

    // here we declare a function object f and try it out
    adder f;
    cout << "f(2, 3) = " << f(2, 3) << endl;

    // transform() expects a function with one argument, so we use
    // bind2nd to make a new function based on f, that takes one
    // argument and adds 5 to it
    transform(a.begin(), a.end(), back_inserter(b), bind2nd(f, 5));

    // output b to see what we got
    cout << "b = [" << endl;
    for (vector<int>::iterator i = b.begin(); i != b.end(); ++i) {
        cout << "  " << *i << endl;
    }
    cout << "]" << endl;

    return 0;
}
Steakhouse answered 30/9, 2008 at 6:59 Comment(5)
Marcin: raj requested the C++ example before I added it. :)Steakhouse
I wish I could vote up a comment trail. This comment chain is amusing. :)Evacuation
Actually, I think this answer is wrong; what this answer describes is "partial application", which the bind functions do. "Currying" is the process of transforming a function taking N arguments into a function taking one argument and returning a function with N-1 arguments, e.g. void (*)(int, short, bool) becomes X (*)(int) with X being void (*)(short, bool). See haskell.org/haskellwiki/Currying for all you ever wanted to know about currying and partial function application.Ozone
@FrerichRaabe You are strictly correct; but the point of currying is to achieve (easy) partial application.Recurvate
@Marcin: I've come to find this question because I want currying and not just any partial application. The difference is that when you have function with 6 arguments carefully written so that you need to fix the first argument, you don't want to put all the placeholders in there!Landholder
L
19

Simplifying Gregg's example, using tr1:

#include <functional> 
using namespace std;
using namespace std::tr1;
using namespace std::tr1::placeholders;

int f(int, int);
..
int main(){
    function<int(int)> g     = bind(f, _1, 5); // g(x) == f(x, 5)
    function<int(int)> h     = bind(f, 2, _1); // h(x) == f(2, x)
    function<int(int,int)> j = bind(g, _2);    // j(x,y) == g(y)
}

Tr1 functional components allow you to write rich functional-style code in C++. As well, C++0x will allow for in-line lambda functions to do this as well:

int f(int, int);
..
int main(){
    auto g = [](int x){ return f(x,5); };      // g(x) == f(x, 5)
    auto h = [](int x){ return f(2,x); };      // h(x) == f(2, x)
    auto j = [](int x, int y){ return g(y); }; // j(x,y) == g(y)
}

And while C++ doesn't provide the rich side-effect analysis that some functional-oriented programming languages perform, const analysis and C++0x lambda syntax can help:

struct foo{
    int x;
    int operator()(int y) const {
        x = 42; // error!  const function can't modify members
    }
};
..
int main(){
    int x;
    auto f = [](int y){ x = 42; }; // error! lambdas don't capture by default.
}

Hope that helps.

Layoff answered 1/10, 2008 at 23:53 Comment(1)
Yeah... this is NOT currying.Malebranche
C
15

Have a look at Boost.Bind which makes the process shown by Greg more versatile:

transform(a.begin(), a.end(), back_inserter(b), bind(f, _1, 5));

This binds 5 to f's second argument.

It’s worth noting that this is not currying (instead, it’s partial application). However, using currying in a general way is hard in C++ (in fact, it only recently became possible at all) and partial application is often used instead.

Checkup answered 30/9, 2008 at 8:44 Comment(6)
Konrad, since you are using the single-source form of std::transform, shouldn't you be using _1 instead of _2? The fact that you wrote bind(f, 5, _1) instead of bind(f, _1, 5) says that the variable gets plugged into f as the second argument.Longways
I cannot recommend Boost.Bind enough -- learn it, use it, love it!!Beckerman
This is not currying. This is partial application.Malebranche
@Thomas You’re absolutely right of course. In fact, I’m super annoyed that the R community uses the wrong terminology fort his. My answer wasn’t actually trying to sell this as currying, but merely showing a better way of implementing the accepted answer. that said, I’ve updated my answer to make this clear.Checkup
Difference between currying and partial application: partial application takes a function like f(x, y) and binds some of the arguments to specific values, returning a function of fewer arguments. E.g. we might bind y to the specific value of 2, and then return a function h(x) = f(x, 2): h is f, partially applied. h(x) does not denote a function; it denotes the value f(x, 2). Currying does not bind specific values. If we curry f(x, y) by removing the y parameter, we end up with a h(x) which returns a function. h(x) denotes a function g, such that g(y) denotes f(x, y).Merrygoround
Thus currying gives us a "partial application factory". We can choose some value like 2 and then write h(2). This will give us a function g(y) which calls f(2, y): the parameter x has been partially applied. We curried f to obtain h, and then called h with a particular value to induce the partial application of f.Merrygoround
O
12

Other answers nicely explain binders, so I won't repeat that part here. I will only demonstrate how currying and partial application can be done with lambdas in C++0x.

Code example: (Explanation in comments)

#include <iostream>
#include <functional>

using namespace std;

const function<int(int, int)> & simple_add = 
  [](int a, int b) -> int {
    return a + b;
  };

const function<function<int(int)>(int)> & curried_add = 
  [](int a) -> function<int(int)> {
    return [a](int b) -> int {
      return a + b;
    };
  };

int main() {
  // Demonstrating simple_add
  cout << simple_add(4, 5) << endl; // prints 9

  // Demonstrating curried_add
  cout << curried_add(4)(5) << endl; // prints 9

  // Create a partially applied function from curried_add
  const auto & add_4 = curried_add(4);
  cout << add_4(5) << endl; // prints 9
}
Orthopedic answered 26/5, 2011 at 11:49 Comment(0)
H
12

If you're using C++14 it's very easy:

template <typename Func, typename... BoundArgs>
auto curry(Func func, BoundArgs... bound_args) {
    return [=](auto... rest_args) {
        return func(bound_args..., rest_args...);
    }; // don't forget semicolumn
}

You can then use it like this:

auto add = [](auto x, auto y) { return x + y; }

// curry 4 into add
auto add4 = curry(add, 4);

add4(6); // 10

If you want it to work with member function pointers, you have to add overloaded version:

template <
    typename    T, typename R, typename... Args,
    typename    P,
    typename... BoundArgs
>
auto curry(R (T::*func)(Args...), P p, BoundArgs... bound_args) {
    return [=](auto... rest_args) {
        return (p->*func)(bound_args..., rest_args...);
    };
}

You can then use it like this:

class Adder
{
public:
    int Add(int x, int y)
        { return x + y; }
};

Adder adder;
auto add4 = curry(&Adder::Add, &adder, 4);
add4(5); // 9

Please note that the above implementations do not use perfect forwarding, therefore bound_args are always copied (not moved).

Haemostatic answered 6/10, 2014 at 10:6 Comment(3)
Technically this is partial function evaluation: a full curry would take a 3 argument function f, and allow curry(f)(3)(2)(1) before invoking. +1, because partial function evaluation is still useful (and often what people mean by 'curry'), and because of how tight it is. You can improve performance with much more code and perfect forwarding.Provenance
...or with only a little bit more code, and moving the arguments from the outer 'factory' function into the returned lambda! ...or, if they are known always to be unmodified, just take const references, and no need to copy or move anything. That said, until C++20, it's a bit painful to move a variadic pack of arguments to the lambda: before that, you have to stash them in a tuple and later apply it.Predicant
@Predicant C++20 is here; can this answer be improved now?Steeplebush
S
10

Some great answers here. I thought I would add my own because it was fun to play around with the concept.

Partial function application: The process of "binding" a function with only some of its parameters, deferring the rest to be filled in later. The result is another function with fewer parameters.

Currying: Is a special form of partial function application where you can only "bind" a single argument at a time. The result is another function with exactly 1 fewer parameter.

The code I'm about to present is partial function application from which currying is possible, but not the only possibility. It offers a few benefits over the above currying implementations (mainly because it's partial function application and not currying, heh).

  • Applying over an empty function:

    auto sum0 = [](){return 0;};
    std::cout << partial_apply(sum0)() << std::endl;
    
  • Applying multiple arguments at a time:

    auto sum10 = [](int a, int b, int c, int d, int e, int f, int g, int h, int i, int j){return a+b+c+d+e+f+g+h+i+j;};
    std::cout << partial_apply(sum10)(1)(1,1)(1,1,1)(1,1,1,1) << std::endl; // 10
    
  • constexpr support that allows for compile-time static_assert:

    static_assert(partial_apply(sum0)() == 0);
    
  • A useful error message if you accidentally go too far in providing arguments:

    auto sum1 = [](int x){ return x;};
    partial_apply(sum1)(1)(1);
    

    error: static_assert failed "Attempting to apply too many arguments!"

Other answers above return lambdas that bind an argument and then return further lambdas. This approach wraps that essential functionality into a callable object. Definitions for operator() allow the internal lambda to be called. Variadic templates allow us to check for someone going too far, and an implicit conversion function to the result type of the function call allows us to print the result or compare the object to a primitive.

Code:

namespace detail{
template<class F>
using is_zero_callable = decltype(std::declval<F>()());

template<class F>
constexpr bool is_zero_callable_v = std::experimental::is_detected_v<is_zero_callable, F>;
}

template<class F>
struct partial_apply_t
{
    template<class... Args>
    constexpr auto operator()(Args... args)
    {
        static_assert(sizeof...(args) == 0 || !is_zero_callable, "Attempting to apply too many arguments!");
        auto bind_some = [=](auto... rest) -> decltype(myFun(args..., rest...))
        {
           return myFun(args..., rest...);
        };
        using bind_t = decltype(bind_some);

        return partial_apply_t<bind_t>{bind_some};
    }
    explicit constexpr partial_apply_t(F fun) : myFun(fun){}

    constexpr operator auto()
    {
        if constexpr (is_zero_callable)
            return myFun();
        else
            return *this; // a callable
    }
    static constexpr bool is_zero_callable = detail::is_zero_callable_v<F>;
    F myFun;
};

Live Demo

A few more notes:

  • I chose to use is_detected mainly for enjoyment and practice; it serves the same as a normal type trait would here.
  • There could definitely be more work done to support perfect forwarding for performance reasons
  • The code is C++17 because it requires for constexpr lambda support in C++17
    • And it seems that GCC 7.0.1 is not quite there yet, either, so I used Clang 5.0.0

Some tests:

auto sum0 = [](){return 0;};
auto sum1 = [](int x){ return x;};
auto sum2 = [](int x, int y){ return x + y;};
auto sum3 = [](int x, int y, int z){ return x + y + z; };
auto sum10 = [](int a, int b, int c, int d, int e, int f, int g, int h, int i, int j){return a+b+c+d+e+f+g+h+i+j;};

std::cout << partial_apply(sum0)() << std::endl; //0
static_assert(partial_apply(sum0)() == 0, "sum0 should return 0");
std::cout << partial_apply(sum1)(1) << std::endl; // 1
std::cout << partial_apply(sum2)(1)(1) << std::endl; // 2
std::cout << partial_apply(sum3)(1)(1)(1) << std::endl; // 3
static_assert(partial_apply(sum3)(1)(1)(1) == 3, "sum3 should return 3");
std::cout << partial_apply(sum10)(1)(1,1)(1,1,1)(1,1,1,1) << std::endl; // 10
//partial_apply(sum1)(1)(1); // fails static assert
auto partiallyApplied = partial_apply(sum3)(1)(1);
std::function<int(int)> finish_applying = partiallyApplied;
std::cout << std::boolalpha << (finish_applying(1) == 3) << std::endl; // true

auto plus2 = partial_apply(sum3)(1)(1);
std::cout << std::boolalpha << (plus2(1) == 3) << std::endl; // true
std::cout << std::boolalpha << (plus2(3) == 5) << std::endl; // true
Schadenfreude answered 31/3, 2017 at 2:9 Comment(0)
R
6

Currying is a way of reducing a function that takes multiple arguments into a sequence of nested functions with one argument each:

full = (lambda a, b, c: (a + b + c))
print full (1, 2, 3) # print 6

# Curried style
curried = (lambda a: (lambda b: (lambda c: (a + b + c))))
print curried (1)(2)(3) # print 6

Currying is nice because you can define functions that are simply wrappers around other functions with pre-defined values, and then pass around the simplified functions. C++ STL binders provide an implementation of this in C++.

Rolo answered 30/9, 2008 at 7:0 Comment(3)
> C++ STL binders provide an implementation of this in C++. yes, but only for unary and binary functions...Ivied
and the broken c++03 (current c++) binders don't work with functions having reference parameters. They simply fail to cope with the reference-to-reference problem. the boost binders are way smarterElectrolysis
@Ivied B-but currying an unary operation is like diluting wine in fermented grappe juice confusedDode
Z
3

I implemented currying with variadic templates as well (see Julian's answer). However, I did not make use of recursion or std::function. Note: It uses a number of C++14 features.

The provided example (main function) actually runs at compile time, proving that the currying method does not trump essential optimizations by the compiler.

The code can be found here: https://gist.github.com/Garciat/c7e4bef299ee5c607948

with this helper file: https://gist.github.com/Garciat/cafe27d04cfdff0e891e

The code still needs (a lot of) work, which I may or may not complete soon. Either way, I'm posting this here for future reference.

Posting code in case links die (though they shouldn't):

#include <type_traits>
#include <tuple>
#include <functional>
#include <iostream>

// ---

template <typename FType>
struct function_traits;

template <typename RType, typename... ArgTypes>
struct function_traits<RType(ArgTypes...)> {
    using arity = std::integral_constant<size_t, sizeof...(ArgTypes)>;

    using result_type = RType;

    template <size_t Index>
    using arg_type = typename std::tuple_element<Index, std::tuple<ArgTypes...>>::type;
};

// ---

namespace details {
    template <typename T>
    struct function_type_impl
      : function_type_impl<decltype(&T::operator())>
    { };

    template <typename RType, typename... ArgTypes>
    struct function_type_impl<RType(ArgTypes...)> {
        using type = RType(ArgTypes...);
    };

    template <typename RType, typename... ArgTypes>
    struct function_type_impl<RType(*)(ArgTypes...)> {
        using type = RType(ArgTypes...);
    };

    template <typename RType, typename... ArgTypes>
    struct function_type_impl<std::function<RType(ArgTypes...)>> {
        using type = RType(ArgTypes...);
    };

    template <typename T, typename RType, typename... ArgTypes>
    struct function_type_impl<RType(T::*)(ArgTypes...)> {
        using type = RType(ArgTypes...);
    };

    template <typename T, typename RType, typename... ArgTypes>
    struct function_type_impl<RType(T::*)(ArgTypes...) const> {
        using type = RType(ArgTypes...);
    };
}

template <typename T>
struct function_type
  : details::function_type_impl<typename std::remove_cv<typename std::remove_reference<T>::type>::type>
{ };

// ---

template <typename Args, typename Params>
struct apply_args;

template <typename HeadArgs, typename... Args, typename HeadParams, typename... Params>
struct apply_args<std::tuple<HeadArgs, Args...>, std::tuple<HeadParams, Params...>>
  : std::enable_if<
        std::is_constructible<HeadParams, HeadArgs>::value,
        apply_args<std::tuple<Args...>, std::tuple<Params...>>
    >::type
{ };

template <typename... Params>
struct apply_args<std::tuple<>, std::tuple<Params...>> {
    using type = std::tuple<Params...>;
};

// ---

template <typename TupleType>
struct is_empty_tuple : std::false_type { };

template <>
struct is_empty_tuple<std::tuple<>> : std::true_type { };

// ----

template <typename FType, typename GivenArgs, typename RestArgs>
struct currying;

template <typename FType, typename... GivenArgs, typename... RestArgs>
struct currying<FType, std::tuple<GivenArgs...>, std::tuple<RestArgs...>> {
    std::tuple<GivenArgs...> given_args;

    FType func;

    template <typename Func, typename... GivenArgsReal>
    constexpr
    currying(Func&& func, GivenArgsReal&&... args) :
      given_args(std::forward<GivenArgsReal>(args)...),
      func(std::move(func))
    { }

    template <typename... Args>
    constexpr
    auto operator() (Args&&... args) const& {
        using ParamsTuple = std::tuple<RestArgs...>;
        using ArgsTuple = std::tuple<Args...>;

        using RestArgsPrime = typename apply_args<ArgsTuple, ParamsTuple>::type;

        using CanExecute = is_empty_tuple<RestArgsPrime>;

        return apply(CanExecute{}, std::make_index_sequence<sizeof...(GivenArgs)>{}, std::forward<Args>(args)...);
    }

    template <typename... Args>
    constexpr
    auto operator() (Args&&... args) && {
        using ParamsTuple = std::tuple<RestArgs...>;
        using ArgsTuple = std::tuple<Args...>;

        using RestArgsPrime = typename apply_args<ArgsTuple, ParamsTuple>::type;

        using CanExecute = is_empty_tuple<RestArgsPrime>;

        return std::move(*this).apply(CanExecute{}, std::make_index_sequence<sizeof...(GivenArgs)>{}, std::forward<Args>(args)...);
    }

private:
    template <typename... Args, size_t... Indices>
    constexpr
    auto apply(std::false_type, std::index_sequence<Indices...>, Args&&... args) const& {
        using ParamsTuple = std::tuple<RestArgs...>;
        using ArgsTuple = std::tuple<Args...>;

        using RestArgsPrime = typename apply_args<ArgsTuple, ParamsTuple>::type;

        using CurryType = currying<FType, std::tuple<GivenArgs..., Args...>, RestArgsPrime>;

        return CurryType{ func, std::get<Indices>(given_args)..., std::forward<Args>(args)... };
    }

    template <typename... Args, size_t... Indices>
    constexpr
    auto apply(std::false_type, std::index_sequence<Indices...>, Args&&... args) && {
        using ParamsTuple = std::tuple<RestArgs...>;
        using ArgsTuple = std::tuple<Args...>;

        using RestArgsPrime = typename apply_args<ArgsTuple, ParamsTuple>::type;

        using CurryType = currying<FType, std::tuple<GivenArgs..., Args...>, RestArgsPrime>;

        return CurryType{ std::move(func), std::get<Indices>(std::move(given_args))..., std::forward<Args>(args)... };
    }

    template <typename... Args, size_t... Indices>
    constexpr
    auto apply(std::true_type, std::index_sequence<Indices...>, Args&&... args) const& {
        return func(std::get<Indices>(given_args)..., std::forward<Args>(args)...);
    }

    template <typename... Args, size_t... Indices>
    constexpr
    auto apply(std::true_type, std::index_sequence<Indices...>, Args&&... args) && {
        return func(std::get<Indices>(std::move(given_args))..., std::forward<Args>(args)...);
    }
};

// ---

template <typename FType, size_t... Indices>
constexpr
auto curry(FType&& func, std::index_sequence<Indices...>) {
    using RealFType = typename function_type<FType>::type;
    using FTypeTraits = function_traits<RealFType>;

    using CurryType = currying<FType, std::tuple<>, std::tuple<typename FTypeTraits::template arg_type<Indices>...>>;

    return CurryType{ std::move(func) };
}

template <typename FType>
constexpr
auto curry(FType&& func) {
    using RealFType = typename function_type<FType>::type;
    using FTypeArity = typename function_traits<RealFType>::arity;

    return curry(std::move(func), std::make_index_sequence<FTypeArity::value>{});
}

// ---

int main() {
    auto add = curry([](int a, int b) { return a + b; });

    std::cout << add(5)(10) << std::endl;
}
Zoes answered 2/1, 2015 at 1:24 Comment(0)
U
2

These Links are relevant:

The Lambda Calculus page on Wikipedia has a clear example of currying
http://en.wikipedia.org/wiki/Lambda_calculus#Motivation

This paper treats currying in C/C++
http://asg.unige.ch/site/papers/Dami91a.pdf

Underpass answered 29/10, 2011 at 18:19 Comment(0)
S
2

The library Boost.Hana provides a pretty handy solution, namely boost::hana::curry, which can be used like this:

#include <boost/hana/functional/curry.hpp>

using boost::hana::curry;
 
int main() {
    constexpr auto add = [](auto x, auto y, auto z) {
        return x + y + z;
    };
 
    static_assert(curry<3>(add)(1)(2)(3) == 6);
    static_assert(curry<3>(add)(1)(2, 3) == 6);
    static_assert(curry<3>(add)(1, 2)(3) == 6);
    static_assert(curry<3>(add)(1, 2, 3) == 6);
}
Skinny answered 2/4, 2023 at 18:16 Comment(0)
Y
0

C++20 provides bind_front for doing currying.

For older C++ version it can be implemented (for single argument) as follows:

template <typename TFunc, typename TArg>
class CurryT
{
private:
    TFunc func;
    TArg  arg ;

public:
    template <typename TFunc_, typename TArg_>
    CurryT(TFunc_ &&func, TArg_ &&arg)
      : func(std::forward<TFunc_>(func))
      , arg (std::forward<TArg_ >(arg ))
        {}

    template <typename... TArgs>
    auto operator()(TArgs &&...args) const
      -> decltype( func(arg, std::forward<TArgs>(args)...) )
        { return   func(arg, std::forward<TArgs>(args)...); }
};

template <typename TFunc, typename TArg>
CurryT<std::decay_t<TFunc>, std::remove_cv_t<TArg>> Curry(TFunc &&func, TArg &&arg)
    { return {std::forward<TFunc>(func), std::forward<TArg>(arg)}; }

https://coliru.stacked-crooked.com/a/82856e39da5fa50d

void Abc(std::string a, int b, int c)
{
    std::cerr << a << b << c << std::endl;
}

int main()
{
    std::string str = "Hey";
    auto c1 = Curry(Abc, str);
    std::cerr << "str: " << str << std::endl;
    c1(1, 2);
    auto c2 = Curry(std::move(c1), 3);
    c2(4);
    auto c3 = Curry(c2, 5);
    c3();
}

Output:

str: 
Hey12
Hey34
Hey35

If you use long chains of currying then std::shared_ptr optimization can be used to avoid copying all previous curried parameters to each new carried function.

template <typename TFunc>
class SharedFunc
{
public:
    struct Tag{}; // For avoiding shadowing copy/move constructors with the
                  // templated constructor below which accepts any parameters.

    template <typename... TArgs>
    SharedFunc(Tag, TArgs &&...args)
      : p_func( std::make_shared<TFunc>(std::forward<TArgs>(args)...) )
        {}

    template <typename... TArgs>
    auto operator()(TArgs &&...args) const
      -> decltype( (*p_func)(std::forward<TArgs>(args)...) )
        { return   (*p_func)(std::forward<TArgs>(args)...); }

private:
    std::shared_ptr<TFunc> p_func;
};

template <typename TFunc, typename TArg>
SharedFunc<
    CurryT<std::decay_t<TFunc>, std::remove_cv_t<TArg>>
>
CurryShared(TFunc &&func, TArg &&arg)
{
    return { {}, std::forward<TFunc>(func), std::forward<TArg>(arg) };
}

https://coliru.stacked-crooked.com/a/6e71f41e1cc5fd5c

Yseulte answered 8/5, 2022 at 10:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.