Transcript Slides
Bartosz Milewski
Concurrency
First class functions
Generic programming
Memory Management (move semantics)
Math nomenclature
Functor
Applicative Functor
Monad
Monoid
for_each(v.begin(), v.end(), [](char c) { cout << c; });
transform(v.begin(), v.end(), w.begin(), [](int x) {
return x * x;
});
Sorting: compare function
Find, copy_if: predicate function
Accumulate: binary function
Currying, partial application: bind
Combining algorithms
v.erase(remove_if(v.begin(), v.end(),
bind(logical_and<bool>(),
bind(greater<int>(), _1, -10),
bind(less<int>(), _1, 10))),
v.end());
Channel for passing data (John Reppy, ML)
Promise
Future
promise<string> prms;
Thread A
Promise
Shared
state
Page 7
Thread B
promise<string> prms;
auto ftr = prms.get_future();
Thread A
Future
Promise
Shared
state
Page 8
Thread B
promise<string> prms;
auto ftr = prms.get_future();
thread th(&thFun, std::move(prms));
Thread A
Thread B
Future
Promise
Shared
state
Page 9
promise<string> prms;
auto ftr = prms.get_future();
thread th(&thFun, std::move(prms));
Thread A
Thread B
Future
Promise
Thread B
prms.set_value(“Hello from future”);
Shared
state
Hello
Page 10
promise<string> prms;
auto ftr = prms.get_future();
thread th(&thFun, std::move(prms));
std::string str = ftr.get();
Thread A
Thread B
Future
Promise
Thread B
prms.set_value(“Hello from future”);
Shared
state
Hello
Page 11
Composability
Orthogonality (Separation of concerns)
Problem: Apply a function to a future
future<string> ftr = async(…);
…
string s = ftr.get(); // blocks?
… then continue to parse(s)
future<string> ftr = async(…);
string s = ftr.get(); // blocks?
… then parse(s)
template<typename F>
auto future::then(F&& func) -> future<decltype(func(*this))>;
future<Tree> fTree = ftr.then([](future<string> fstr) {
return parse(fstr.get()); // doesn’t block
});
Tree tree = fTree.get(); // blocks?
Next combinator
future<Tree> fTree = ftr.next(parse);
Tree tree = fTree.get(); // blocks?
future<string> fStr = …
future<Tree> fTree = fStr.next(parse);
next “lifts” parse to act on futures
vector<int> v = {1, 2, 3};
vector<int> w;
w.resize(v.size());
transform(v.begin(), v.end(), w.begin(), square);
» transform “lifts” square to act on containers
Unconstrained parametric polymorphism
(universally quantified types)
For all types T:
template<class T> class future;
template<class T> class vector;
template<class T> class unique_ptr;
A mapping of types:
T -> future<T>
Type constructor
Function lifting: then, transform, (Haskell: fmap)
T -> future<T>
fuction<S(T)> -> function<future<S>(future<T>));
Problem: Composing (chaining) async calls
future<HANDLE> async_open(string &);
future<Buffer> async_read(HANDLE fh);
In principle, this is the result:
future<future<Buffer>> ffBuf =
async_open("foo").next(&async_read);
Collapse two levels of future
async_open("foo.cpp").next(&async_read).unwrap().n
ext(&async_process).unwrap();
Combination of next and unwrap called bind
(Haskell: >>=, bind combines join with fmap)
In C++, next (then) can be overloaded to serve as bind
Usage: conditional asynchrony, recursion
A future that is ready
make_ready_future
future<int> fint = make_ready_future<int>(42);
int i = fint.get(); // doesn’t block
Analogously, for containers:
vector<int> v = {42};
Functor pattern
Type constructor
Function lifting (then, next, transform)
Collapsing (unwrap, concat)
Value lifting (make_ready_future)
Type constructor
Value lifting: make_ready_future()
bind: combination of .next(f).unwrap() [or an
overload of next]
Usage of the future monad pattern:
Composing libraries of async functions
It’s all in the wrist
next (or bind) checks for exceptions and propagates
them (without calling the continuation)
At the end of the chain, recover from exception
async_open("foo.cpp").next(&async_read).next(parse).r
ecover(&on_error);
Exception monad
Implements short-circuiting
Can be put on top of the future monad (monad
transformers)
Problem: Need N futures to proceed.
Create a barrier, get all values, proceed.
when_all: takes futures, returns future of finished
futures
Client gets, iterates, gets each, and proceeds with values
Functional approach
Apply a regular function of n argument to n futures.
Lifting of n-ary functions
when_all_done(futures).next(fn)
Together with make_ready_future: applicative functor
Problem: Wait for the first future to complete
when_any: takes futures, returns a future of futures, at
least one of them ready
Client gets, iterates, checks is_ready, picks value.
proceeds
Functional approach
The OR combinator (like addition?)
Combines two futures into one
Assoc.: (f1 OR f2) OR f3 = f1 OR (f2 OR f3)
Neutral element: the “never” future
never OR f = f = f OR never
Defines a monoid
New patterns based on functional programming
Functor
Applicative Functor
Monad
Monoid
Composability and orthogonality
Result: Library of futures