Yet another tutorial on Monads(,Monoids, and Functors with Scala)
Upfront, this post is really shallow, only defining terms. It does not go into the application and consequences of these objects. But these notes are useful to me because it explains these terms directly involving Category Theory where necessary. It does not go into depths about how monads can convert imperative to functional code, or that the consequence monoid associativity is parallelism. Honestly, I feel the other tutorials already do a good job of that.
Monad in terms of monoids and functors
A monad can be described as 1 a monoid whose objects are functors 2 In Scala, an implementation of one of the minimal sets of monadic combinators, satisfying the laws of associativity and identity (Monad Laws)
Monoid
The following are two valid definitions of a monoid
- A category with exactly one object
- An algebra (recall algebra is just objects and operations) that consists of
- objects of some type
A
- an associative binary operation, ie say
op
whereop(op(x,y), z)
=op(x, op(y, z))
andx
,y
,andz
are of typeA
- An
identity
orzero
object whereop(x,zero)
=op(zero, x)
=x
- objects of some type
Remember, either definitions are valid. You do not need to know Category Theory to know what a monoid is.
In Scala, a monoid can be expressed with this trait
trait Monoid[A] {
def op(a1: A, a2: A): A
def zero: A
}
Examples of monoids include
- type
Bool
, associative binary operation&&
, id objecttrue
- type
Bool
, associative binary operation||
, id objectfalse
- type
String
, opn string concatenation+
, id object empty string""
- type
List
, opn list concatenation+
, id object empty listNil
Functor
If A and B are categories, a functor from A to B is an assignment of
- an object Φ(A) in B for every object A in A
- an arrow Φ(f) : Φ(A) -> Φ(A’) in B for every arrow A -> A’ in A
subject to the following equations:
In Scala, most objects with a map
method are functors.
And finally, an example of a monoid whose objects are functors:
- type
A -> List[B]
, opn:List.flatMap
egval x: A -> List[B] = _.map(f) val y: B -> List[C] = _.map(g) val z: C -> List[D] = _.map(h) // op(op(x,y),z) = op(x,op(y,z)) x.flatMap(y).flatMap(z) == x.flatMap(a => y(a).flatMap(z))
, id object_.map(_=>_)
“Monad laws”, “Monoid laws”, “Monadic Combinators”
The associativity and identity laws are “Monad laws” also “Monoid laws”.
Let’s revisit the definition of a monad in Scala, an implementation of one of the minimal sets of monadic combinators, satisfying the laws of associativity and identity (Monad Laws).
Implementations of these minimal sets of monadic combinators satisfy the laws of associativity and identity.
unit
andflatMap
unit
andcompose
unit
,map
, andjoin
where unit
is the identity
/ zero
object, and join
has function signature
def join[A](mma: F[F[A]]): F[A]