14
FP in Scala Walk with monsters (ADTs)

Fp in scala with adts

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

Page 1: Fp in scala with adts

FP in ScalaWalk with monsters (ADTs)

Page 2: Fp in scala with 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)

Page 3: Fp in scala with adts

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

Page 4: Fp in scala with adts

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)]

Page 5: Fp in scala with adts

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] = ???

Page 6: Fp in scala with adts

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?

Page 7: Fp in scala with adts

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

Page 8: Fp in scala with adts

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

Page 9: Fp in scala with adts

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

Page 10: Fp in scala with adts

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.

Page 11: Fp in scala with adts

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.

Page 12: Fp in scala with adts

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))

}

Page 13: Fp in scala with adts

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

Page 14: Fp in scala with adts

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)

}