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