When coming from an imperative background, learning Scala and Functional Programming in general is initially quite difficult. I found that it’s not as simple as matching things you know into a new syntax, but rather requires a fundamental change in the way you think about writing code. There are plenty of amazing resources online to do this if you are willing to put in the time and effort such as the coursera courses: Functional Programming Principles in Scala and follow up: Principles of Reactive Programming.

For this reason, it’s quite easy to skip over the mathematical underpinnings of FP just so that you can get to writing real code quickly. After a decent amount of time coding in Scala, you will be hard pressed to avoid coming across the concept of Monads. The Scala standard library offers a lot of incredibly useful monads such as Future, Option, Try and so on. If you have spent enough time second guessing your sanity to understand monads properly, you’re in the minority. Most people end up with “It’s some kind of burrito container thing. Aw who cares, I can map and flatMap them and it does stuff for me”.

It turns out there is a whole branch of mathematics behind abstract concepts like monads, and I set out to try and get my head around the fundamentals. After reading a lot of bad explanations of things like Functors, Semigroups and Applicatives, I feel as though I’ve finally wrapped my head around the essence and usefulness of these concepts enough to explain it in a relative straightforward way. (As always, relevant xkcd).

To begin, I will refer to an incredibly useful diagram from Typeclassopedia on a simplified heirachy of the fundamental concepts from a presentation on scalaz’s motivations and uses. I’m only going to focus on Functors, Applicative Functors, Semigroups, Monoids and then touch on Monads at the end of the blog. It would be useful to refer back to this with each new concept I introduce to get a feel for where it fits into the heirachy.

I will assume that you are at least superficially familiar with monads or have at least used them in Scala or haskell, but for the most part will avoid referring to them until we get up to the discussion on them.

First of all, lets look at Functors.

According to wikipedia: “In mathematics, a functor is a type of mapping between categories which is applied in category theory. Functors can be thought of as homomorphisms between categories. In the category of small categories, functors can be thought of more generally as morphisms.”

My GOD. No wonder people don’t bother learning this stuff properly. Explanations are littered with incredibly precise definitions and assume that you already understand what “categories”, “homomorphisms”, “small categories”, “morphisms” before you can even begin to understand what a functor even is.

I’m going to take a step back and give you a simplified understanding using Scala as a reference. This will sacrifice a bit of precision of the definition to improve the clarity. So lets take a look at the Functor definition in Scala.

trait Functor[F[_]] { def map[A, B](fa: F[A])(f: A => B): F[B] }

This simply means that a functor is parameterised by some type F[T] where we don’t care what T is. It also defined a map method. So what does this mean? From the looks of it, we can deduce that a functor is something we can map, but what does that actually imply?

Quick detour – If we consider two sets A and B with elements a∈A and b∈B. We need a total function (which just means that every a∈A produces some b∈B) that maps from A => B. This is contrasted with a Partial Function which maps from a subset of A to B. In Scala A => B implies a total function – every input value has an output value.

So we can say based off the definition of Functor that it is some parameterised container whose value can be mapped from A => B. It would be a reasonable generalisation to say anything that can “map”, (though functions are technically functors as well and they don’t technically “map”, but they do transform some value from some set A to some set B (A => B)).

The more correct way of defining it is that a functor allows the application of a pure function (A => B) to occur within a context (F[_]).

(Don’t worry if that made as much sense american patent law, I’ll come back to this notion of a “context” in the discussion about Applicative Functors).

Now you might be thinking at this point “I get it, but why is that useful?”. The obvious answer is that it’s a really fundamental abstraction that lets you know that the thing you’re dealing with is “mappable”. It would be much like in java, defining a base interface with “map” on it and then having all the subclasses define their version of map. If you deal with these classes at the interface level, you know that they are mappable. It also means that you can pick the right level of abstraction for different concepts. e.g. instead of using a monad because it lets you “map stuff”, you can choose a Functor because that is actually the contract you expect.

Now that we have a reasonable understanding of functors, lets take a look at Applicative Functors.

We saw before that a functor “allows the application of a pure function (A => B) to occur within a context (the context of F[_])”. An applicative functor is for the cases where you need to apply a function within a context to a value within a context. If this doesn’t make sense yet, don’t worry, we will go into a bit more detail. Once again lets start by looking at the definition:

trait Applicative [ F [ _ ]] extends Functor { def funcMap[A, B](fa: F[A])(f: F[A => B]): F[B] }

I’ve invented the name funcMap because I find <*> (from haskell) an incredibly unintuitive thing to name a function. As we can see, as with Functor, an Applicative Functor defines some kind of mapping between two spaces A and B. The difference is in the function f: F[A => B] that is applied.

So a Functor maps from A => B whereas an applicative functor has a mapping from A => B as the type parameter for some F such that F[A => B]. What does this mean?

It simply means that you have a mapping from A => B within the context of F[_]. But what is meant by “context” and how does that apply?

val opt: Option[Int] = Option(3) val optFunc: Option[Int => Int] = Option((x: Int) => x + 5)

We can see here that we have a value “3” and that value is within an Optional context. The optional context is such that, until we look inside, we don’t know if it is a Some or a None, and thus the context of the Optional value determines the way we interact with it. The same thing can happen with a function. Above we have optFunc which defines a function “(x: Int) => x + 5” in the context of an Option[_]. If we try to apply the contents of the Option, and its a None, it will behave differently to if it had been a Some. This is what is meant by “within a context”.

So what an applicative lets us do is apply something like optFunc to opt, whilst taking their contexts into account. If we had tried to do this with a Functor directly, we would not have been able to compile the code. In Scala, Option is in fact a Monad, so it is quite trivial to do the above operation, however when we are only dealing with Functors, this new level of abstraction is needed.

Truth be told, I haven’t really come across many real world uses of Applicative Functors other than scalaz Validation. This is quite a useful use case as monads have a concept of ordering of operations that applicative functors do not, and as such do not really model the intention of validation. For example if you want to validate that 5 properties meet some predicate and are not dependent on one another, we would ideally use applicative functors because they impose no sequential ordering like monads do.

Next lets take a look at SemiGroups:

A semigroup is a very simple concept and much like a Functor, can allow us to reason about our underlying code. I’ve found in particular that semigroups and monoids (which we’ll get to shortly) are very useful when considering things like Map Reduce pipelines. But lets first take a look at what they are.

A semigroup is simply a set S with elements of type T combined with a binary operation that acts on two elements s1,s2∈S such that (s1 operation s2) produces some output of type T. Note here that the output may or may not be present in the set S.

So we could define a trait as we have before to look something like this:

trait Semigroup[T] { def operation(s1: T, s2: T): T }

A simple example of a semigroup is multiplication where T = Int. e.g.

class IntegerSemigroup extends Semigroup[Int] { override def operation(s1: Int, s2: Int): Int = s1 * s2 }

The last important part to understand about a semigroup is that its associative. This means that for some values s1,s2,s3∈S, the equation (s1 operation s2) operation s3 == s1 operation (s2 operation s3) holds true. I mentioned earlier that building map reduce pipelines can benefit from knowing when you are dealing with semi groups. The reason for this is that if you know something is a semigroup, you instantly know what you can parallelise it so long as you maintain relative ordering. When you combine the results from each parallel operation execution, they will be the same as if you did them sequentially.

Next up lets take a look at Monoids:

A monoid is very easy to understand once you understand a semigroup. A monoid is very literally a semigroup that has what is referred to as “identity”. Identity is formally defined as:

for a set S, i ∈ S such that for any s ∈ S, (i operation s) == (s operation i) == s

I think this is best explained with an example. Given a simple Monoid definition:

trait Monoid[F] extends Semigroup[F] { def identity: F }

we can define a Monoid on type Int, with the operation of multiplication. (Always remember that a Monoid is a type + operation. Integer addition for example is a different monoid to integer multiplication). To be “correct” the types in scala represent a mathematical set of values. Integer for example represents the set of all integers (within the bounds of what can be represented by the Integer type).

To define integer multiplication, we need only to define an identity element. Lets take some Int “3” and multiply it by “1”. We know quite obviously that this returns “3”. Furthermore we can show that the previous definition for identity holds with the number 3 and the operation * and the identity element 1, since 3 * 1 == 1 * 3 == 3. I am not going to prove that this holds for the entire set of possible integers but you should (at least empirically) be quite confident that that case is true.

As such, we would define a monoid with multiplication operation over Integers to have an identity of “1”. Can you think what the identity element might be for addition over integers? (Hint: It’s not the same as multiplication).

So as you can see, the semigroup and monoid abstractions are quite powerful and let us reason about things in very useful way. One thing to keep in mind is that there are lots of implicit things you need to consider when defining the set of values that a monoid is valid over. As I mentioned before for example, integer multiplication is obviously a monoid mathematically, but its technically not realisable when using Int type since we cannot represent INT_MAX * INT_MAX using an Int (due to overflow). These are obviously edge cases that apply to lots of different things in software engineering but its worth keeping in mind when applying these mathematical concepts to the real world.

Congratulations if you’ve made it this far! We only have the big bad Monad left to discuss. Now that we have all of these fundamental concepts under our belt though, it should be much easier to follow the more involved parts of the monad definitions.

Monads:

At a high level there are plenty of very useful tutorials on monads, and how to conceptualise them such as “monads are burritos”, “monads are programmable semicolons”, “monads are containers”, so I won’t try and reinvent the wheel there. I personally think of them as all of these depending on the context. A monad is certainly a container, but thats not all it is. It can be though of as a programmable semicolon, but thats not all it is. It can be though of as a burrito, but that just makes me hungry, and its hard to program when I’m hungry. It turns out that wikipedia actually has a reasonably good definition of a monad that whilst incomplete actually covers the crux of what it is. “A monad is a structure that represents computations defined as a sequence of steps: a type with a monad structure defines what it means to chain operations or nest functions of that type together”.

Hopefully if you’ve followed the rest of this blog thus far, you will be able to find a lot of excellent discussions of what monads are that are written by people much smarter than me. I will defer to them, but whilst reading their explanations, keep in mind where it fits in the heirachy we have discussed.

I hope this has been of use to people.