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