Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Combining functions that return futures

Tags:

c++

future

Suppose I have two functions that return futures:

std::future<T> foo(int);

std::future<U> bar(T const &);

I want to combine the two functions to a function that takes an int as parameter and returns a std::future<U>. How should I write this function? Is it possible to generalize function composition for functions that return futures?

std::future<U> foobar1(int x)
{
  auto foo_x = foo(x);
  return bar(foo_x.get());
}

This function will block until the future returned by foo is finished, right? This is clearly not what I want.

std::future<U> foobar2(int x)
{
  return std::async([=]()
  {
    auto foo_x = foo(x);
    return bar(foo_x.get()).get();
  });
}

It feels silly to call get() on the future returned by bar just to have it turned into a new future by std::async

std::future<U> foobar3(int x)
{
  return foo(x).then([](std::future<T> f)
  {
    return bar(f.get()).get();
  };
}

Here, again I have to call get() on the future returned by bar otherwise I'd have a future<future<U>>. Is this the correct approach?

like image 747
MadScientist Avatar asked Jan 28 '15 13:01

MadScientist


1 Answers

What you want is a monad. std::future is almost but not quite a monad but lacks the ability to properly compose functions by lifting them as you have noticed. For a detailed discussion on this please see this blog post.

The summary is that for C++17 an additional method has been proposed for std::future: either next or then which would take a function to be applied to the future's value once it is available. This would be roughly equivalent to bind in Haskell for example (note: not std::bind).

Let's take the two functions:

T foo(int);
U bar(T const &);

Here we have a mapping int -> T -> U, what you want is to lift this mapping to int -> std::future<T> -> std::future<U>. What this might look like would be:

int x = 2;
std::async([=](){return foo(x);}).then(bar);

Where then is a function that automatically maps T -> U to std::future<T> -> std::future<U>.

Disclaimer: I am not a Haskell programmer and realise I have probably got everything mixed up. I gladly welcome corrections.

like image 54
sjdowling Avatar answered Oct 29 '22 01:10

sjdowling