Functional programming in C++. Implementing f(a)(b)(c)

C++C++11Functional ProgrammingCurryingStd Function

C++ Problem Overview


I have been getting into the basics of functional programming with C++. I am trying to make a function f(a)(b)(c) that will return a + b + c. I successfully implemented the function f(a)(b) which returns a + b. Here is the code for it:

std::function<double(double)> plus2(double a){
    return[a](double b){return a + b; };
}

I just cannot figure out how to implement the function f(a)(b)(c) which as I previously stated should return a + b + c.

C++ Solutions


Solution 1 - C++

You can do it by having your function f return a functor, i.e., an object that implements operator(). Here is one way to do it:

struct sum 
{
	double val;
	
	sum(double a) : val(a) {}
	
	sum operator()(double a) { return val + a; }
	
	operator double() const { return val; }
};

sum f(double a)
{
	return a;
}

##Example

Link

int main()
{
	std::cout << f(1)(2)(3)(4) << std::endl;
}

#Template version You can even write a templated version that will let the compiler deduce the type. Try it here.

template <class T>
struct sum 
{
	T val;

	sum(T a) : val(a) {}
	
	template <class T2>
	auto operator()(T2 a) -> sum<decltype(val + a)> { return val + a; }
	
	operator T() const { return val; }
};

template <class T>
sum<T> f(T a)
{
	return a;
}

##Example

In this example, T will ultimately resolve to double:

std::cout << f(1)(2.5)(3.1f)(4) << std::endl;

Solution 2 - C++

Just take your 2 elements solution and expand it, by wrapping it with another lambda.

Since you want to return a lambda that get a double and returns a doubles' addition lambda, all you need to do is to wrap your current return type with another function, and add a nested lambda into your current one (a lambda that returns a lambda):

std::function<std::function<double(double)>(double)> plus3 (double a){
    return [a] (double b) {
        return [a, b] (double c) {
            return a + b + c;
        };
    };
}


  • As @Ðаn noted, you can skip the std::function<std::function<double(double)>(double)> and get along with auto:

      auto plus3 (double a){
          return [a] (double b) {
              return [a, b] (double c) { return a + b + c; };
          };
      }
    
  • You can expand this structure for every number of elements, using deeper nested lambdas. Demonstration for 4 elements:

      auto plus4 (double a){
          return [a] (double b) {
              return [a, b] (double c) {
                  return [a, b, c] (double d) {
                      return a + b + c + d;
                  };
              };
          };
      }
    

Solution 3 - C++

Here is a slightly different approach, which returns a reference to *this from operator(), so you don't have any copies floating around. It is a very simple implementation of a functor which stores state and left-folds recursively on itself:

#include <iostream>

template<typename T>
class Sum
{
    T x_{};
public:
    Sum& operator()(T x)
    {
        x_ += x;
        return *this;
    }
    operator T() const
    {
        return x_;
    }
};

int main()
{
    Sum<int> s;
    std::cout << s(1)(2)(3);
}

Live on Coliru

Solution 4 - C++

This isn't f(a)(b)(c) but rather curry(f)(a)(b)(c). We wrap f such that each additional argument either returns another curry or actually invokes the function eagerly. This is C++17, but can be implemented in C++11 with a bunch of extra work.

Note that this is a solution for currying a function - which is the impression that I got from the question - and not a solution for folding over a binary function.

template <class F>
auto curry(F f) {
    return [f](auto... args) -> decltype(auto) {
        if constexpr(std::is_invocable<F&, decltype(args)...>{}) {
            return std::invoke(f, args...);
        }
        else {
            return curry([=](auto... new_args)
                    -> decltype(std::invoke(f, args..., new_args...))
                {
                    return std::invoke(f, args..., new_args...);
                });
        }
    };  
}

I've skipped forwarding references for brevity. Example usage would be:

int add(int a, int b, int c) { return a+b+c; }

curry(add)(1,2,2);       // 5
curry(add)(1)(2)(2);     // also 5
curry(add)(1, 2)(2);     // still the 5th
curry(add)()()(1,2,2);   // FIVE

auto f = curry(add)(1,2);
f(2);                    // i plead the 5th

Solution 5 - C++

The simplest way I can think of to do this is to define plus3() in terms of plus2().

std::function<double(double)> plus2(double a){
    return[a](double b){return a + b; };
}

auto plus3(double a) {
    return [a](double b){ return plus2(a + b); };
}

This combines the first two argument lists into a single arglist, which is used to call plus2(). Doing so allows us to reuse our pre-existing code with minimal repetition, and can easily be extended in the future; plusN() just needs to return a lambda that calls plusN-1(), which will pass the call down to the previous function in turn, until it reaches plus2(). It can be used like so:

int main() {
    std::cout << plus2(1)(2)    << ' '
              << plus3(1)(2)(3) << '\n';
}
// Output: 3 6

Considering that we're just calling down in line, we can easily turn this into a function template, which eliminates the need to create versions for additional arguments.

template<int N>
auto plus(double a);

template<int N>
auto plus(double a) {
    return [a](double b){ return plus<N - 1>(a + b); };
}

template<>
auto plus<1>(double a) {
    return a;
}

int main() {
    std::cout << plus<2>(1)(2)          << ' '
              << plus<3>(1)(2)(3)       << ' '
              << plus<4>(1)(2)(3)(4)    << ' '
              << plus<5>(1)(2)(3)(4)(5) << '\n';
}
// Output: 3 6 10 15

See both in action here.

Solution 6 - C++

I'm going to play.

You want to do a curried fold over addition. We could solve this one problem, or we could solve a class of problems that include this.

So, first, addition:

auto add = [](auto lhs, auto rhs){ return std::move(lhs)+std::move(rhs); };

That expresses the concept of addition pretty well.

Now, folding:

template<class F, class T>
struct folder_t {
  F f;
  T t;
  folder_t( F fin, T tin ):
    f(std::move(fin)),
    t(std::move(tin))
  {}
  template<class Lhs, class Rhs>
  folder_t( F fin, Lhs&&lhs, Rhs&&rhs):
    f(std::move(fin)),
    t(
      f(std::forward<Lhs>(lhs), std::forward<Rhs>(rhs))
    )
  {}
  template<class U>
  folder_t<F, std::result_of_t<F&(T, U)>> operator()( U&& u )&&{
    return {std::move(f), std::move(t), std::forward<U>(u)};
  }
  template<class U>
  folder_t<F, std::result_of_t<F&(T const&, U)>> operator()( U&& u )const&{
    return {f, t, std::forward<U>(u)};
  }
  operator T()&&{
    return std::move(t);
  }
  operator T() const&{
    return t;
  }
};

It takes a seed value and a T, then permits chaining.

template<class F, class T>
folder_t<F, T> folder( F fin, T tin ) {
  return {std::move(fin), std::move(tin)};
}

Now we connect them.

auto adder = folder(add, 0);
std::cout << adder(2)(3)(4) << "\n";

We can also use folder for other operations:

auto append = [](auto vec, auto element){
  vec.push_back(std::move(element));
  return vec;
};

Use:

auto appender = folder(append, std::vector<int>{});
for (int x : appender(1)(2)(3).get())
    std::cout << x << "\n";

Live example.

We have to call .get() here because for(:) loops doesn't understand our folder's operator T(). We can fix that with a bit of work, but .get() is easier.

Solution 7 - C++

If you are open to using libraries, this is really easy in Boost's Hana:

double plus4_impl(double a, double b, double c, double d) {
    return a + b + c + d;
}

constexpr auto plus4 = boost::hana::curry<4>(plus4_impl);

And then using it is just as you desire:

int main() {
    std::cout << plus4(1)(1.0)(3)(4.3f) << '\n';
    std::cout << plus4(1, 1.0)(3)(4.3f) << '\n'; // you can also do up to 4 args at a time
}

Solution 8 - C++

All these answers seem terribly complicated.

auto f = [] (double a) {
    return [=] (double b) {
        return [=] (double c) {
            return a + b + c;
        };
    };
};

does exactly what you want, and it works in C++11, unlike many or perhaps most other answers here.

Note that it does not use std::function which incurs a performance penalty, and indeed, it can likely be inlined in many cases.

Solution 9 - C++

Here is a state pattern singleton inspired approach using operator() to change state.

Edit: Exchanged the unnecessary assignment for an initialization.

#include<iostream>
class adder{
private:
  adder(double a)val(a){}
  double val = 0.0;
  static adder* mInstance;
public:
  adder operator()(double a){
    val += a;
    return *this;}
  static adder add(double a){
    if(mInstance) delete mInstance;
    mInstance = new adder(a);
    return *mInstance;}
  double get(){return val;}
};
adder* adder::mInstance = 0;
int main(){
  adder a = adder::add(1.0)(2.0)(1.0);
  std::cout<<a.get()<<std::endl;
  std::cout<<adder::add(1.0)(2.0)(3.0).get()<<std::endl;
  return 0;
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionGigaxelView Question on Stackoverflow
Solution 1 - C++JonasView Answer on Stackoverflow
Solution 2 - C++UrielView Answer on Stackoverflow
Solution 3 - C++vsoftcoView Answer on Stackoverflow
Solution 4 - C++BarryView Answer on Stackoverflow
Solution 5 - C++Justin Time - Reinstate MonicaView Answer on Stackoverflow
Solution 6 - C++Yakk - Adam NevraumontView Answer on Stackoverflow
Solution 7 - C++JustinView Answer on Stackoverflow
Solution 8 - C++Tom SwirlyView Answer on Stackoverflow
Solution 9 - C++mathreadlerView Answer on Stackoverflow