How can currying be done in C++?

Viewed 32113

What is currying?

How can currying be done in C++?

Please Explain binders in STL container?

11 Answers

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;
}

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.

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.

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++.

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

Related