Estimated reading time: 14 minutes
We, developers, hate and love things continually. We start to take great pleasure using those things that make the "aha!" in our heads quickly. e.g. patterns, libraries and language features. But those we do not follow swiftly we discard them and sometimes hate them. The same happens with sophisticated terms which some functional hippie guys try to sell us in places like conferences or meetups, terms that seem to come from The Lord of the Rings.
We are developers, we speak semicolons and curly braces right? So why should these words matter? If you are involved with the Scala world maybe you already have heard concepts like Monad, Functor and many others coming from mathematics.
For the next few minutes, let’s discuss why I think it is important to know a quite bit about the ideas behind these notions. We are going to tackle Monad, Functor, Natural Transformation and we will see how they are related to for comprehension and composition in Scala boundaries.
This article will not be another Monad tutorial or Monad introduction due to my very limited knowledge on this and because there are already so many other articles about it that could make more justice to explaining Monad. Certainly, it is possible to write code not knowing about dark, antique math fundamentals. We have been doing it like this for many years.
So of course Scala language is not an exception, you can write code as traditionally as it has been done for decades.
With Scala you can gain huge benefits like conciseness and maintainability writing imperative code style, you just need follow OOP principles and follow best practices and naming conventions. You can write less verbose code than Java at least.
However, if your daily job uses Scala you probably prefer functional style, and who doesn't? We enjoy avoiding mutations, doing recursion and using Scala collections to do things like:
Since Scala obstruct deals directly with null values it provides to us utilities to deal with values which might not exist, one of them is
Option for example which possesses a repertoire of gadgets similar to the collections, like:
In both examples above there is an obvious common pattern that we see very often in our Scala code.
flatMap that we heavily used in
Seq or in other parametric types like
Future, are there because all these type constructors are Monads and Functors.
Note: we are going to use type constructor and parametric type to refer to the same concept. An
Option[T]is a type constructor because it itself is not a type, we need to pass it a concrete type to create the final type e.g String to create an Option[String].
Let's define what a Monad is and, as I said above, this would not be a Monad tutorial. I will neither give you a very strict definition nor repeat what other Monad articles say such as "Monads are just chicken burritos o chicken wraps where the chicken is the type parameter". No. I just want to give a definition that suffices our Scala context and not one that would provoke a heart attack to some mathematicians who are already resting in their graves.
So we are going to say that a Monad
M is a parametric type
M[T] with two operations,
unit, that has to satisfy some laws.
In functional literature,
flatMap is more commonly known as
bind and has the following signature:
As Scala is both Functional and Object Oriented, the
flatMap function is defined inside a
trait or a
class, so we can use the parameter
m: M[T] in the implementation to refer to the instance itself which would be of type
On the other hand,
unit is just a constructor function that goes from
T => M[T]) in Scala each Monad has different names for it
unit operation. e.g:
List is a Monad with unit(x) = List(x)
Set is a Monad with unit(x) = Set(x)
Option is a Monad with unit(x) = Some(x)
Now let's see Functors.
F is a parametric type
F[T] with a
map operation. That has to satisfy some laws.
In other functional languages
map usually has a signature like
map(x: F[T], f: T => U): F[U] but again the fact that Scala is OO and
map operation always is defined on Functor itself we omit
x: F[T] and use
this in the body of the operation. Some Functors in Scala.
List is a Functor with map(f: T => U):List[U] = List("1").map(e => e.toInt)
Set is a Functor with map(f: T => U):Set[U] = Set(2).map(_ + 2)
Option is a Functor with map(f: T => U):Option[U] = Option(true).map(_.toString).map(_.toUpperCase)
In both definitions I mention laws, Monad laws and Functors laws. I think it's not necessary to talk about those laws because: first, I don't want us to get bored; and second, as I said we don't need a rigid definition for our (Scala) interests.
So what are Monads good for? Despite of what we usually believe, Monad is not about side effect or values that can be absent or values that will be available at some point the time. No, Monads are about composition-- just simple composition! For that reason its main operation (flatMap) is also known as bind, as I said before.
I once read that Monads were like semicolons in programming languages, we use them not just to delimit lines but to compose sentences sequentially.
With Monad we compose expressions that are passing their result downstream while we manipulate them in an harmonic way.
Scala has a built-in feature that is dedicated entirely to Monads. That is
yield is more related with Haskell's
return than Java's
for-each. Regardless of what we tend to think
yield has nothing to do with collections but with Monad.
This is the same example of the top re-written using
yield obtaining the same result.
yield can be thought as tweezers and magnifying glassess that allow us to look inside Monads, pick-up the contained value, transform it and drop it back to another Monad.
We can use
yield for any Monad/Functor parametric type, in other words any type that has
unit operations will be available for us to use it with
There is no need for it to be a type constructor coming in the Standard Library. We can use it with custom types created from scratch.
Let's see a runnable exercise in the Scala Repl.
We are going to define a
case class with the two operations
map inside it. Remember that case classes provide us an implicit
apply constructor function, so that would be the
We can see how our
GateToWorld type works with for-comprehension doing this simple composition
Compiler will look at
flatMap/map to extract values from objects
three in the
for block and it will figure out the
unit function to use it in the
yield block to construct a new Monad value with 6 in it. (
new GateToWorld would be
The idea with Monad patterns is to try to assemble all our programs to avoid the need to deal with the contained value. For that reason, getters functions in Monad have scary names like unsafePerformIO, or other simples names that somehow using also terrifies us because we know they are the devil and can break our programs entirely, like
Let's do another exercise using our same custom useless Monad
GateToWorld. The idea would be a tiny program with Inputs and Outputs to show how Monad fit in the entirely flow. So before we are going to refactor our Monad a bit to hide the contained value and define a getter method with an scary name to send a warning to whomever wants to access the value using it.
Now let's define a data model and some operations for our business.
We are going to compose a program which will ask for data to the employee, will do some stupid data transformation (normalizeEmployee) and will "save" that data in the "database" (faking it obviously)
Now let's run the program
As we could see
for-comprehension and Monads (any parametric type with
flatMap/map/unit operations) will give us, in my opinion, a wonderful way to deal with composition.
However, composition in Monad is restricted to each Monad type constructor, that means that we can not do something like:
Therefore we can't also do
That is because
List, for example, is defined to be something like
f parameter needs to be a function from
List[B] or any subtype of
GenTraversableOnce[B] (which is a supertype of List) so type system will complain if we try to put something like a function that goes from
Either[B] that is the case of
Right.apply (one of the
unit operations for
Of course saying of above we can resolve this very simple
But we are going to complicate it a bit and introduce another fancy term. We said that
Seq[_] are Functors and Monads. So operations that go from one Functor to another Functor are called Natural Transformation. Exactly like
toSeq that we just used to convert
Either[_,Int] Functor to
a natural transformation provides a way of transforming one functor into another while respecting the internal structure. Wikipedia
Scala Standard Library's Functors already come with some Natural Transformation like
While we usually represent functions like this
f: A -> B,
g: B -> C in the functional and mathematical literature. A Natural Transformation is represented with a curly arrow as follows
f: A ~> B,
g: B ~> C just to denote that A,B and C are functors,(
A[_], B[_], C[_]).
For that reason, and the fact that Scala type system is crazy (in a good sense) and powerful, we might have seen in libraries like Scalaz and Cats, this curly arrow and others funny operators.
We will borrow a fragment of Scalaz Natural Transformation implementation just to see how they squeeze Scala type system to allow operators look like in the mathematical literature.
Scalaz Natural Transformation
As you can see they definitely complicate it. You will find many operations that already exist in the Scala Standard Library shown in these other libraries, Scalaz and Cats, in a different way. Another example is Monad's
flatMap/bindthat we talked above, you might find it with the alias
def >>=[B](f: A => M[B]):M[B] = flatMap(f).
I must confess although I am quite a fan of those awkwards operators, they don't help in an early research of these concepts.
So get back to Natural Transformation, the simple one, what is it good for?
Natural Transformation in addition with
flatMap can be very useful to flatten types.
How many of us have found ourselves struggling when using an API that returns something like:
When we just need a simple
List[Employee], we usually resolve it with a lot of boilerplate code, with a lot of overusing pattern matching and with a lot of tedium.
But let see another way to deal with these nested types, flattening them, with
flatMap and Natural Transformation.
We said that what we just need a List of Employees, but don't forget that there can be exceptions also in that return type, so ideally we should log them somehow.
First let's stay with a List of Employee
Now let's go for the errors
There are better approaches than this, of course, like groupBy for example. I just wanted an use case to flatten.
The process of flatten can be thought of in two steps: first, we need to transform all inner parametric types to the most outer (in this case to List) later we need to apply flatMap from the outer to the most inner (in this case the Option).
Of course there are many other scenarios where Natural Transformation makes sense.
To conclude we had mentioned terms like Monad, Functors and we saw how they are intrinsically related with some implementation of Scala features.
We looked at how
Natural Transformation are means of composition.
I consider that studying and knowing about these ideas share the same importance as studying OOP patterns from books like e.g Gang of Four Design Patterns.
So don't be shy to say Category theory outloud and don't be mad if you listen to it.
The worst that could happen is that this guy appears if you say it three times.
And how cool would be that.