My Attempt at Monads
Leon Tager
Zhia Chong
Software Engineer
Medium Blogger
(bit.ly/zhiastory)
Goals
Learn about
- Semigroup
- Monoids
- Functors
- Monoids
In a Explain-to-a-5-year-old manner

How did we get here?
☑ Immutable code
☑ Composing functions
☑ Higher order functions
☑ Currying
☑Recursion
At the start


Summingbird
Streaming MapReduce with Scalding and Storm
Monoids?
Scala
Lightweight, modular, and extensible library for functional programming

Monads?


Help!
infixl 1 >>, >>=
class Monad m where
(>>=) :: m a -> (a -> m b) -> m b
(>>) :: m a -> m b -> m b
return :: a -> m a
fail :: String -> m a
m >> k = m >>= \_ -> k
This doesn't help


functor
Alt
Plus
Alternative
Apply
Applicative
Monad
Bifunctor
Extend
Comond
Profunctor
Traversable
Foldable
Semigroupoid
Category
Setoid
Ord
Contravariant
Filterable
Semigroup
Monoid
Group
functor
Monad
Semigroup
Monoid
Semigroups
It's just an operation
Semigroups
It's just an operation



Semigroups - what is it?
// here's our definition of a semigroup
trait Semigroup[A] {
// some binary operation for you to define
def op(x: A, y: A): A
}
And...
Semigroup rules
Associativity - order of operation doesn't matter
// (1 + 2) + 3 = 1 + (2 + 3)
op(x, op(y, z)) = op(op(x, y), z)
Closure - operation on members of a set always makes a member of the same set
// (A, A) -> A
def op(x: A, y: A): A
Examples of Semigroup
val intMaxSemigroup: Semigroup[Int] = new Semigroup[Int] {
def op(x: Int, y: Int): Int = math.max(x, y)
}
val intMinSemigroup: Semigroup[Int] = new Semigroup[Int] {
def op(x: Int, y: Int): Int = math.min(x, y)
}
val list = Seq(1, 2, 3)
list.reduce(intMaxSemigroup.op) // 3
list.reduce(intMinSemigroup.op) // 1
// But what will happen here?
val list = List.empty[Int]
list.reduce(intMaxSemigroup.op)
java.lang.UnsupportedOperationException: empty.reduceLeft
at scala.collection.LinearSeqOptimized.reduceLeft(scratch_10.scala:131)
at scala.collection.LinearSeqOptimized.reduceLeft$(scratch_10.scala:130)
at scala.collection.immutable.List.reduceLeft(scratch_10.scala:82)
at scala.collection.TraversableOnce.reduce(scratch_10.scala:204)
at scala.collection.TraversableOnce.reduce$(scratch_10.scala:204)
at scala.collection.AbstractTraversable.reduce(scratch_10.scala:100)
at #worksheet#.get$$instance$$res0(scratch_10.scala:19)
at #worksheet#.#worksheet#(scratch_10.scala:51)
// here's our definition of a semigroup
trait Semigroup[A] {
// an associative operation
def op(x: A, y: A): A
}
// Subtraction is not associative
scala> ((1 - 2) - 3)
res0: Int = -4
scala> (1 - (2 - 3))
res1: Int = 2
Examples of a non-Semigroup
Semigroup
Do stuff to two things
Monoids
It's just an operation with a default
Monoids
It's just an operation with a default


Monoids - what is it?
// Monoid, like a semigroup is a definition of an operation
// with an identity value
trait Monoid[A] {
def op(a1: A, a2: A): A
def empty: A
}
And...
Monoid rules
Associativity - order of operation doesn't matter
// (1 + 2) + 3 = 1 + (2 + 3)
op(x, op(y, z)) = op(op(x, y), z)
Closure - operation on members of a set always makes a member of the same set
// (A, A) -> A
def op(x: A, y: A): A
Identity
// 1 + 0 = 0 + 1 = 1
op(x, id) == op(id, x) == x
Familiar?
If op meets the rules on type M then we say M forms a monoid under op.
Terminology
If addition meets the rules on type natural numbers then we say natural numbers form a monoid under addition.
Examples of a monoid
// but wait, there's more
trait Monoid[A] extends Semigroup[A] {
def empty: A
}
val stringMonoid = new Monoid[String] {
def op(a1: String, a2: String) = a1 + a2
def empty = ""
}
val intMultiplication = new Monoid[Int] {
def op(x: Int, y: Int) = x * y
val empty = 1
}
val list = Seq(1, 2, 3)
list.foldLeft(intMultiplication.empty)(intMultiplication.op) // 6
val emptyList = List.empty[Int]
emptyList.foldLeft(intMultiplication.empty)(intMultiplication.op) // 1
Much better
// here's our definition of a monoid
trait Monoid[A] {
// an associative operation
def op(a1: A, a2: A): A
// an identity element
def empty: A
}
Monoid
semigroup with an identity
Monoids
Semigroup
Monoid - in other words
Monoid -why should I care?
Closure, Associativity and Identity
Monoids are everywhere.
-
Thanks to closure, we can combine and aggregate things.
-
Thanks to associativity, we can do it in parallel.
-
Thanks to identity, we know what to do when we don't have anything to start off with.

Monoid - How we use it at Twitter


Functors
Take this and turn it into that




Functors - what is it?
Functors - what is it?
// It's a wrapper
trait Functor[F[_]] {
// It has a map method
def map[A,B](fa: F[A])(f: A => B): F[B]
}
And...
Functor rules
Composition
// Mapping with g and then f is the same as
// Mapping both at the same time.
functor.map(g).map(f) == functor.map(x => f(g(x)))
Identity
// passing the identity function
// returns the same functor back
// (in the mathematical sense)
functor.map(x => x) == functor
// Example for an option
// (Basically the same old option.map)
val functorForOption: Functor[Option] = new Functor[Option] {
def map[A, B](fa: Option[A])(f: A => B): Option[B] = fa match {
case None => None
case Some(a) => Some(f(a))
}
}
val option = Option(1)
functorForOption.map(option)(a => a + 1) // Option(2)
// definition of a functor
trait Functor[F[_]] {
// It has a map method
def map[A,B](fa: F[A])(f: A => B): F[B]
}
Examples of functors
Examples of non-functors
// Breaks the identity law
val notAFunctor: Functor[List] = new Functor[List] {
def map[A, B](fa: List[A])(f: A => B): List[B] = fa match {
case _ => List.empty[B]
}
}
val list = List(1, 2, 3)
notAFunctor.map(list)(x => x) // res1: List[Int] = List()
Examples of non-functors
// Breaks the composition law
val notAFunctor2: Functor[Option] = new Functor[Option] {
var prop = 0
def map[A, B](fa: Option[A])(f: A => B): Option[B] = fa match {
case None => None
case Some(a) => {
prop += 1
if (prop == 1) Some(f(a)) else None
}
}
}
val f: Int => Int = x => x + 1
val g: Int => Int = y => y + 2
notAFunctor2.map(Option(1))(z => f(g(z))) //Option[Int] = Some(4)
notAFunctor2.map(
notAFunctor2.map(Option(1))(g)
)(f) // None :-(
Functor in pictures

Monads
Take this and turn it into that ++



Monads - what is it?


Monads - what is it?
And...
// It's a wrapper
trait Monad[F[_]] {
// It has two methods
def unit[A](a: => A): F[A]
def flatMap[A, B](ma: F[A])(f: A => F[B]): F[B]
}
Monad rules
Associative
// Mapping with f and then g is the same as
// Mapping both at the same time.
x.flatMap(f).flatMap(g) == x.flatMap(a => f(a).flatMap(g))
Identity
// Left identity
flatMap(x)(unit) == x
// Right identity
flatMap(unit(y))(f) == f(y)
Examples of monads
You already use them
- List
- Future*
- Option
- Try
- Either
Monads
Functors
Monads - in other words
Monad - use cases
def getUser(id: Long): Future[User]
def getHistory(user: User): Future[History]
def getHistoryById(id: Long): Future[History] = getHistory(getUser(id))
def getHistoryById(id: Long) = getUser(id) match {
case Success(user) => getHistory(user)
case Failure(_) => this.asInstanceOf[Future[S]]
}
def getUser(id: Long): Option[User]
def getHistory(user: User): Option[History]
def getHistoryById(id: Long) = getUser(id) match {
case Some(user) => getHistory(user)
case None => None
}
Lists, Try, Either? This is going to be a long night
Monad - use cases
def getUser(id: Long): Future[User]
def getHistory(user: User): Future[History]
def getHistoryById(id: Long): Future[History] = getUser(id).flatMap(getHistory)
// Which is the same as
val res: Future[History] = for {
u <- getUser(0)
h <- getHistory(u)
} yield h
Monads are great when evaluation of your function depends on the value of the previous function
Monad
Functor with a flatMap
Monad - fun fact
Functors compose. Monads don't
// Have you ever had to deal with
Option[List[A]]
// Have you ever tried to map over it?
optionalList.map(_.map(f))
Turns out that if F and G are functors, so is F[G[_]] for any functor
Composition of monads does not guarantee the result is a monad.

Things that confused me
Sometimes it's map, sometimes it's >>
Sometimes it's flatMap, sometimes it's join
Sometimes it's flatMap, sometimes it's join
trait Monad[F[_]] {
def unit[A](a: => A): F[A]
def flatMap[A, B](ma: F[A])(f: A => F[B]): F[B]
}
trait Monad[F[_]] {
def unit[A](a: => A): F[A]
def map[A,B](fa: F[A])(f: A => B): F[B]
def join[A](fa: F[F[A]]): F[A]
}
trait Monad[F[_]] {
def unit[A](a: => A): F[A]
// Composing Kleisli arrows
def compose[A,B,C](f: A => F[B], g: B => F[C]): A => F[C]
}
A monad needs to satisfy the rules mentioned earlier - associativity and identity. The interface is not important.
I can't find any of these interfaces in Scala
Summary of key points
Semigroup | An operation on two objects | op: (A, A) -> A |
Monoid | A an operation with a default | op: (A, A) -> A identity: A |
Functor | Something that can be mapped over | map F[A] ->(A -> B): F[B] |
Monad | A functor that unwrapes the top layer | flatMap F[A] -> (A -> F[B]): F[B] |
A monad is just a monoid in the category of endofunctors, what's the problem?
Thank you
My attempt at monads
By Leon Tager
My attempt at monads
- 202