Upload
hang-zhao
View
164
Download
1
Embed Size (px)
DESCRIPTION
Function Programming in Scala. A lot of my examples here comes from the book Functional programming in Scala By Paul Chiusano and Rúnar Bjarnason, It is a good book, buy it.
Citation preview
FP in ScalaWalk with monsters (ADTs)
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)
Applicative FunctorFunctor
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
Applicative FunctorFunctor def oneVarFunc: Int => Int =
{
_ + 1
}
val x1 = Some(1)
x1.map(oneVarFunc)
get Some(2)
Monadval 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)]
Why Applicative Functor 1. Because it is less restrictive, it in fact easier to reason.2. It is also a key part of Traversable ADT3. Not all Applicative functor is Monad
a. Indefinite length Streamb. 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] = ???
Monad TransformerWant 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?
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
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
Foldabletrait 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 MapReduceWith Applicative and Foldable, we will introduce Traversable
Foldableval 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.
TraversableTraversable is a generalized FoldableFoldable 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.
Traversabletrait 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))
}
TraversableAs 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
Traversable in Actionimport 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)
}