Monads allow us to chain continuations
Chaining continuations means that we can execute a series of functions, where the output of one function is the input of the next. Check out the following example of chaining high-order functions:
cars := LoadCars()
for _, car := range cars.Filter(ByHasNumber()).
Filter(ByForeign()).
Map(Upgrade()).
Reduce(JsonReducer(cars), Collection{}) {
log.Println(car)
}
You will see the following output:
{"car": {"make": "Honda", "model": " Accord ES2 LX"}}
{"car": {"make": "Lexus", "model": " IS250 LS"}}
{"car": {"make": "Lexus", "model": " SC 430 LS"}}
{"car": {"make": "Toyota", "model": " RAV4 EV"}}
How much more code would be required if we were to implement the for loops, error checking, and other scaffolding that is typically required when coding Go in the typical imperative style of programming?
Instead of telling Go how to filter, map, and reduce our collection, we declare what we want to accomplish. Later in this chapter, we do implement the Filter, Map, and Reduce functions, but what if the Go standard library already provides these for us?
How can we expect Go to provide HOF implementations for cars? That would not be reasonable, right? What's missing? The answer is generics.
The ChainLink implementation in this chapter is sort of a poor man's monad. We'll explore a real monad in the last chapter of this book and discover that there are more operations involved (Bind, Return, monadic error handling). Real monads also do not rely on global variables. What is similar is that they both allow us to execute operations in order, where the output of one function is the input to the next. That is a key concept to remember.