10 Monads

 

This chapter covers

  • Understanding functors
  • Going one step further from transform with monads
  • Composing functions that return wrapper types
  • Handling asynchronous operations in FP style

The functional programming world isn’t big on design patterns, but common abstractions pop up everywhere. These abstractions allow you to handle different types of problems from significantly different domains in the same way.

In C++, we already have one abstraction like this: iterators. With ordinary arrays, we can use pointers to move around and access data. We can use the operators ++ and -- to go to the next and previous elements, respectively, and we can dereference them with the * operator. The problem is, this works only for arrays and structures that keep their data in a contiguous chunk of memory. It doesn’t work for data structures such as linked lists, sets, and maps implemented using trees.

For this purpose, iterators were created. They use operator overloading to create an abstraction that has the same API as pointers, which can work not only for arrays but for various different data structures. Iterators also work for things such as input and output streams that aren’t traditionally considered data structures.

10.1 Not your father’s functors

10.1.1 Handling optional values

10.2 Monads: More power to the functors

10.3 Basic examples

10.4 Range and monad comprehensions

10.5 Failure handling

10.5.1 std::optional<T> as a monad

10.5.2 expected<T, E> as a monad

10.5.3 The Try monad

10.6 Handling state with monads

10.7 Concurrency and the continuation monad

10.7.1 Futures as monads

10.7.2 Implementations of futures

10.8 Monad composition

Summary