Monoid, Functor, Applicative and Monad
Monoid
Given a Monoid trait Semigroup
trait Semigroup[M] {
def append(a: M, b: M): M
val zero: M
}
the following should hold:
append(a, append(b, c)) === append(append(a, b), c)
append(a, zero) = a
append(zero, a) = a
Monoid examples:
Int
with+
and0
Int
with*
and1
Boolean
with||
andfalse
A => A
withcompose
andidentity
List[A]
with++
andNil
String
with+
and""
Functor
Concept
Functor is a type class that defines how to apply a function to a value wrapped in a context(T
).List
, Option
, Ethier
, Try
both are functor.
trait Functor[T[_], A] {
def fmap[B](f: A => B): Functor[T, B]
def id: T[A]
}
the Functor takes two type parameters, T[_]
which is a generic type, and a type A
one concrete example is:
//List as T, A as A
case class ListFunctor[A](val id: A, xs: List[A]) extends Functor[List, A] {
def fmap[B](f: A => B): List[B] = ListFunctor(xs.map(f))
}
Functor laws:
-
fmap id = id
if we map the
id
function over a functor, the functor that we get back should be the same as the original functor -
for any functor F, the following should hold:
fmap (f . g) F = fmap f (fmap g F)
composing two functions and then mapping the resulting function over a functor should be the same as first mapping one function over the functor and then mapping the other one
Function is Functor:
Function composition:
Mapping a Function over a Function will produce a new Function(function composition), just like mapping a function over a List
will produce a List
, mapping a function over a Option
will produce a Option
.
Lifting:
Given a map
function with type (A => B) => F[A] => F[B]
(F
is a functor, it could be List
, Option
, or Ethier
), we can think the map
as a function which take a function (with type A => B
) as parameter and return a new function just like the old one(with type F[A] => F[B]
).
Applicative
Concept
Applicative is a type class that defines how to apply a function tf
wrapped in a context T
to a value wrapped in a context T
.
trait Applicative[T[_], A] extends Functor[T, A] {
def apply[B](f: T[A => B]): Applicative[T, B]
}
Monad
Concept
Monad is a type class Monad[T[_], A]
that defines how to apply a function that returns a wrapped value A => T[B]
to a wrapped value T[A]
.
trait Monad[T[_], A] extends Monoid[T, A] with Applicative[T, A] {
def flatMap[B](f: A => T[B]): Monad[T, B]
}
Monad law:
-
Left identity
Given a value
x
and a functionf
, the following should hold:
unit(x) flatMap f = f(x)
- Right identity
Given a monad m
, the following should hold:
m flatMap unit = m
- Composition
Given a monad m
and two functions f
and g
, the following should hold:
m flatMap f flatMap g == m flatMap g flatMap f
A concrete Monad example
case class ListMonad[A](val list: List[A]) extends Monad[List, A] {
//defined in Monoid
override def append(values: List[A]): ListMonad[A] = ListMonad(list ++ values)
//defined in Monoid
override def id: List[A] = Nil
//defined in Functor
override def fmap[B](f: (A) => B): ListMonad[B] = ListMonad(list.map(f))
//defined in Applicative
override def apply[B](tf: List[(A) => B]): ListMonad[B] = ListMonad(list.map(tf.head))
//defined in Monad
override def flatMap[B](f: (A) => List[B]): ListMonad[B] = ListMonad(list.flatMap(f))
}