"It's Just Another Manic Monad" - Susanna Hoffs
There's an old adage in the FP space - as soon as you gain an understanding of monads, you lose the ability to explain them to anyone else.
Fortunately I have a limited understanding, and so hopefully this will allow me to occasionally drift into lucidity.
Rather than diving deep into a ton of stuff about category theory, I want to try a different approach and look at what is a monad in Scala and what that means in practical terms
My god, it's full of monads
Basically anything in Scala that wraps an object (or objects) of another type is a monad. So that mean List, Map and basically all the collection types, Option, Future, Either ... and so on.
Consider a List[String]
. It's a monad wrapping 0 or more Strings.
A monad must have 2 operations, one to construct a monad from the wrapped type, and one to apply a function to each of the wrapped value and return a monad of the same type.
The first operation is pretty straightforward - it's the apply()
method which, in the case of our List, is sugared as List(x)
The second operation is somewhat more invloved. Known as bind, or in Scala flatMap, it takes a function that operates over the values in the monad and produces a monad of the same type (but with values of a possibly different type).
If we look at the signature of flatMap on List it is (approximately) as follows
def flatMap[B](f: (A) => List[B]): List[B]
Dissecting this we see that flatmap takes a function that outputs a List[B]
as it's argument, and returns a List[B]
As an aside, one of the challenges I find with functional programming is that everything is abstracted. I'm sure that method signatures with generic types are enough for some people to grok a new concept, but I need examples with concrete types.
So what does flatmap mean?
Suppose we have a List of recording artists names in the form of Strings, and a function that takes a recording artist name as a String and produces a List of albums they have made.
val recordingArtists = List("The Bangles")
val getAlbums: String => List[String] = ???
recordingArtists.flatMap(getAlbums) // List("All Over the Place", "Different Light", "Everything"...
In our example the types A and B are the same type - String, and what flatMap is doing is mapping each value in the input to it's output from function f. It is then flattening the result - hence flatMap.
It's the flattening part that's of interest to us as Scala devs. Let's look at another example via another aside.
One of the things I love about Scala is the lack of null
. OK you can use null
if you really want to, but I think that's perverse, since we have a few tricks that are way better.
Suppose we have a calculation that may produce a result, or it may not. Maybe you want to look up an artist and the function may return their band if it knows about them or it may not. For this we have the type Option
which produces a Some(value)
or None
So our function application may look like
f("Susanna Hoffs") // Some("The Bangles")
f("Stefan Compton") // None
And suppose we have a variant of our previous function that gets the best album from that band, if they have produced any
val getBestAlbum: String => Option[String]
Now we can use flatMap to compose these functions and get a result
f("Susanna Hoffs").flatMap(getBestAlbum) // Some("Album Title")
Note that we've done this in a single line that's very clear in intent and meaning and without needing any null checks or the possibility of blowing up with a null pointer exception. I'd call that a result.
All this is really scratching the monadic surface, but hopefully none of it is particularly scary. I shall sign off with a John von Neumann quote, slightly mdified by me to better fit.
“Young man, in mathematics computing you don’t understand things. You just get use d to them.”
Top comments (0)