The document discusses Applicative Functors, Monads, Foldables and Traversables in Scala. Some key points:
1. Applicative Functors allow applying functions to values inside data structures in a point-free style, while preserving the structure.
2. Monads are more powerful than Applicative Functors as they can flatten nested structures and change behavior through chaining of functions.
3. Foldables allow aggregating values inside a data structure using a Monoid. Traversables generalize Foldables by keeping the structure intact using an Applicative instead of a Monoid.
4. Composition of Applicative Functors is possible generically, but composition of
2. Applicative Functor
// id function:
// def id[A](a: A): A = a
// compose function:
// def compose[A,B,C](f: B => C, g:
A => B): A => C =
// a => f(g(a))
trait Functor[F[_]] {
def map[A,B](fa: F[A])(f: A =>
B): F[B]
}
// Functor Law
// identity: map(x)(id) == x
// composition: map(a)(compose(f,
g)) == map(map(a,g), f)
trait Applictive[F[_]] extends Functor
[F] {
def unit[A](a: => A): F[A]
def ap[A,B](la: F[A])(f: F[A => B]): F
[B]
override def map[A, B](la: F[A])(f: A
=> B): F[B] =
ap(la)(unit(f))
}
// Applicative Law
// identity: ap(a, unit(id)) == a
// composition: ap(ap(a, g), f) == ap(a, ap
(g, ap(f, unit(compose))))
// homomorphism: ap(unit(a), unit(f)) ==
unit(f(a))
// interchange: ap(unit(a), f) == ap(f, unit(f
=> f(x)))
trait Monad[F[_]] extends Applictive[F] {
def unit[A](a: => A): F[A]
def flatMap[A,B](ma: F[A])(f: A => F[B]): F[B]
override def ap[A,B](la: F[A])(f: F[A => B]): F
[B] =
flatMap(f)(t1 => flatMap(la)(t2 => unit(t1
(t2))))
override def map[A,B](ma: F[A])(f: A => B): F
[B] =
flatMap(ma)(a => unit(f(a)))
}
// Monad Law
// left identity: f(a) == flatmap(unit(a), f)
// right identity: a == flatMap(a, x => unit(x))
// associativity: flatMap(a, x => flatMap(f(x), g)) ==
flatMap(flatMap(a, f), g)
3. Applicative Functor
Functor
Take one function
with one input and
apply onto the
value inside
content
Monad
Take function(s)
that can apply to
values inside
contents, and also
can change
behavior in the
process of apply
function(s)
Applicative Functor
Take one function
with multiple input
and apply onto the
values inside
contents
4. Applicative Functor
Functor
def oneVarFunc: Int => Int =
{
_ + 1
}
val x1 = Some(1)
x1.map(oneVarFunc)
get Some(2)
Monad
val x1 = Some(1)
val x2 = Some(2)
val x3 = Some(3)
x1.flatMap { r1 => {
r1 match {
case 1 => x2.flatMap {
r2 => Some(r1 * r2)
}
case _ => x3.flatMap {
r3 => Some(r1 + r3)
}
}
}
get Some(2)
Applicative Functor
def twoVarFunc: (Int, Int) => Int = {_ + _}
val x1 = Some(1)
val x2 = Some(2)
val x3 = None
x2.ap(x1.map(twoVarFunc.curried))
get Some(3)
x3.ap(x2.map(twoVarFunc.curried))
get None
def zip[U](that: Future[U]): Future[(T, U)]
5. Why Applicative Functor
1. Because it is less restrictive, it in fact easier to reason.
2. It is also a key part of Traversable ADT
3. Not all Applicative functor is Monad
a. Indefinite length Stream
b. Multidimensional Array
4. Different Applicative functor can compose, different
Monad can not compose (has to use Monad
Transformer)
a. def compose[A,B,C](f: A => M[B], g: B => M[C]): A =>
M[C] = ???
6. Monad Transformer
Want a Applicative/Monad that has multiple
property by compose 2 or more
Applicatives/Monads, for example.
List[Option[Future[Int]]]
Can this type be also a Applicative/Monad, has ap
and flatMap defined. Can we just have generic way
to do this instead of write one for every type?
7. Applicative Composition
trait Functor[F[_]] {
def map[A,B](fa: F[A])(f: A => B): F[B]
}
trait Applictive[F[_]] extends Functor[F] {
def unit[A](a: => A): F[A]
def ap[A,B](la: F[A])(f: F[A => B]): F[B]
override def map[A, B](la: F[A])(f: A => B): F[B] =
ap(la)(unit(f))
def apply2[A, B, C](fa: => F[A], fb: => F[B])(f: (A, B) => C): F
[C] =
ap(fb)(map(fa)(f.curried))
}
trait CompositionApplicative[F[_], G[_]] extends Applicative
[({type λ[α] = F[G[α]]})#λ] {
implicit def F: Applicative[F]
implicit def G: Applicative[G]
def ap[A, B](fa: => F[G[A]])(f: => F[G[A => B]]): F[G[B]]
=
F.apply2(f, fa)((ff, ga) => G.ap(ga)(ff))
def unit[A](a: => A): F[G[A]] = F.unit(G.unit(a))
}
Yes, we can do it for Applicative in a generic way
8. Monad Composition
trait Monad[F[_]] extends Applictive[F] {
def unit[A](a: => A): F[A]
def flatMap[A,B](ma: F[A])(f: A => F[B]): F[B]
override def ap[A,B](la: F[A])(f: F[A => B]): F[B] =
flatMap(f)(t1 => flatMap(la)(t2 => unit(t1(t2))))
override def map[A,B](ma: F[A])(f: A => B): F[B] =
flatMap(ma)(a => unit(f(a)))
}
case class OptionT[M[_],A](value: M[Option[A]]) {
self =>
def unit(a: A)(implicit m: Monad[M]): OptionT[M, A] =
new OptionT[M, A](m.unit(Some(a)))
def flatMap[B](f: A => OptionT[M, B])(implicit m: Monad[M])
: OptionT[M, B] = new OptionT[M, B](
m.flatMap(self.value) {
case None => m.unit(None)
case Some(a) => f(a).value
})
}
No, we can not do it for Monad in a generic way
9. Foldable
trait Semigroup[A] {
def op(a: A, b: A): A
}
trait Monoid[A] extends Semigroup[A] {
val zero: A
}
trait Foldable[F[_]] {
def foldMap[A,B](fa: F[A], f: A => B)(implicit m: Monoid
[B]): B
/*
def fold[M: Monoid](t: F[M]): M // also called reduce with
variance
def foldRight[A, B](t: F[A], z: => B, f: (A, B) => B): B
def foldLeft[A, B](t: F[A], z: B, f: (B, A) => B): B
def foldr1[A, B](t: F[A], f: (A, => A) => A): Option[A]
def foldl1[A, B](t: F[A], f: (A, A) => A): Option[A]
*/
}
This the definition of
Monoid and Foldable,
simple and generic but very
very useful.
In fact, it is the conceptual
base for MapReduce
With Applicative and
Foldable, we will introduce
Traversable
10. Foldable
val IntMonoid = new Monoid[Int] {
def op(a: Int, b: Int): Int = a * b
val zero: Int = 1
}
val ListFodable = new Foldable[List] {
def foldMap[A, B](t: List[A], f: A => B)(implicit m: Monoid[B]): B =
t.foldRight(m.zero)((a,b) => m.op(f(a), b))
}
object test {
val x1 = List(1,2,3,4)
val r1 = ListFodable.foldMap(x1, (x: Int) => x)(IntMonoid)
}
Foldable use a Monoid to
go through a structure, and
in the process return some
aggregated value with the
original structure collapsed.
11. Traversable
Traversable is a generalized Foldable
Foldable use a Monoid to go through a structure, and in the
process return some aggregated value with the original
structure collapsed.
Traversable use a Applicative go through a structure, and
in the process return some aggregated value with the
original structure kept.
12. Traversable
trait Foldable[F[_]] {
def foldMap[A, M: Monoid](t: F[A], f: A => M): M
}
trait Applicative[F[_]] extends Functor[F]{
def unit[A](a: => A): F[A]
def ap[A,B](fa: F[A])(fab: F[A => B]): F[B]
override def map[A,B](t: F[A])(f: A => B): F[B] =
ap(t)(unit(f))
}
type Const[A, B] = A
implicit def monoidApplicative[M](m: Monoid[M]) =
new Applicative[({ type f[x] = Const[M, x] })#f] {
def unit[A](a: => A): M = m.zero
override def ap[A,B](m1: M)(m2: M): M = m.op
(m1, m2)
}
import scala.Predef.identity
trait Traversable[T[_]] extends Functor[T] with Foldable[T] {
def traverse[F[_]: Applicative, A, B](f: A => F[B], t: T[A]): F[T[B]] // =
sequence(map(t)(f))
def sequence[F[_]: Applicative, A](tfa: T[F[A]]): F[T[A]] = traverse(identity
[F[A]], tfa)
// def mapM[M[_]: Monad, A, B](f: A => M[B], t: T[A]): M[T[B]] = ???
// def sequenceM[M[_]: Monad](tmb: T[M[B]]): M[T[B]] = ???
type Id[A] = A
val Identity = new Applicative[Id] {
def unit[A](a: => A) = a
def ap[A,B](a: A)(f: A => B): B = f(a)
}
override def map[A, B](k: T[A])(f: A => B) = traverse[Id, A, B](f, k)
(Identity)
override def foldMap[A, M](as: T[A], f: A => M)(implicit m: Monoid[M]):
M=
traverse[({type f[x] = Const[M,x]})#f,A,Nothing](f, as)(monoidApplicative
(m))
13. Traversable
As you can see, transverse preserves the structure, it is the
strength and weakness.
The sequence method is very interesting, F[G[A]], if F is a
Traversable, and G is a Applicative, it in fact can be
reversed as G[F[A]]
It also works with Monad as every Monad is a Applicative
(does not means Monad composable, as you need a
Traversable)
Traversable is composable, like Applicative
14. Traversable in Action
import scala.language.higherKinds
val OptionApplicatable = new Applicative[Option] {
def unit[A](a: => A) = Some(a)
def ap[A,B](a: Option[A])(f: Option[A => B]): Option[B] =
f.flatMap {
t1 => a.flatMap {
t2 => unit(t1(t2))
}
}
}
val ListTraversable = new Traversable[List] {
def traverse[F[_], A, B](f: A => F[B], t: List[A])(implicit m:
Applicative[F]): F[List[B]] =
t.foldRight(m.unit(List[B]()))((a, fbs) => m.zip(f(a),fbs)(_
:: _))
}
object test {
val x1 = List(1,2,3,4)
val x2 = List(Option(1), Option(2), Option(3))
val x3 = List(Option(1), Option(2), Option(3), Option(null))
def f1(a: Int): Option[Int] = Some(a)
val r1 = ListTraversable.traverse(f1, x1)(OptionApplicatable)
val r2 = ListTraversable.sequence(x2)(OptionApplicatable)
val r3 = ListTraversable.sequence(x3)(OptionApplicatable)
}