Unit 35: Loggable
After this unit, students should understand:
- why we need
flatMapoperation.
So far in the class, we have seen very general abstractions that support the flatMap operation. But, it is not clear where this operation comes from, why is it fundamental, nor why is it useful1
In this unit, we are going to build a general abstraction step-by-step, get stuck at some point, and see how flatMap comes to our rescue, and hopefully, through this exercise, you will get some appreciation of flatMap.
Function Composition
Let's start with some methods that we wish to operate over int. Let's use some trivial functions so that we don't get distracted by its details.
1 2 3 4 5 6 7 | |
These methods are pure functions without side effects, they take in one argument and produce a result.
Just like mathematical functions, we can compose them together in arbitrary order to form more complex operations.
1 2 | |
Loggable with Pair
Suppose now we want to return not only an int, but some additional information related to the operation on int. For instance, let's suppose we want to return a string describing the operation (for logging). Java does not support returning multiple values, so let's return a Pair.
1 2 3 4 5 6 7 | |
Now, we can't compose the methods as cleanly as before. This is because the return value of absWithLog is a Pair<Integer,String> but incrWithLog accepts an int as its parameter.
1 | |
We will need to change our methods to take in Pair<Integer,String> as the argument.
1 2 3 4 5 6 7 | |
We can now compose the methods.
1 | |
Loggable Class
Let's do it in a more OO-way, by writing a class to replace Pair.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | |
We can use the class above as:
1 2 | |
Note that we can now chain the methods together to compose them. Additionally, the log messages get passed from one call to another and get "composed" as well.
Making Loggable general
There are many possible operations on int, and we do not want to add a method fooWithLog for every function foo. One way to make Loggable general is to abstract out the int operation and provide that as a lambda expression to Loggable. This is what the map method does.
1 2 3 | |
We can use it like:
1 | |
We can still chain the methods together to compose them.
But, map allows us to only apply the function to the value. What should we do to the log messages? Since the given lambda returns an int, it is not sufficient to tell us what message we want to add to the log.
To fix this, we will need to pass in a lambda expression that takes in an integer, but return us a pair of integer and a string, in other words, return us a Loggable. We call our new method flatMap.
1 2 3 4 | |
By making flatMap takes in a lambda that returns a pair of integer and string, Loggable can rely on these lambda to tell it how to update the log messages. Now, if we have methods like this:
1 2 3 4 5 6 7 | |
We can write:
1 2 3 | |
to now compose the methods incr and abs together, along with the log messages!
Making Loggable More General
We started with an operation on int, but our Loggable class is fairly general and should be able to add a log message to any operation of any type. We can make it so by making Loggable a generic class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | |
-
This note is inspired by The Best Introduction to Monad. Another excellent notes on category theory is by Bartosz Milewski ↩