FP in Scala 
the exploratorium for monads (part 2)
for comprehension (review) 
Use the OO/FP hybrid style List code 
we can do 
def test = { 
val a = List(1,2) 
val b = List(3,4) 
for { 
t1 <- a 
t2 <- b 
} yield ((t1,t2)) 
} 
We get 
[ (1,3) (1,4) (2,3) (2,4) ]
for comprehension (review) 
It is in fact flatMap/map in disguise, it is same as 
def test = { 
val a = List(1,2) 
val b = List(3,4) 
a.flatMap { 
t1 => b.map { 
t2 => (t1,t2) 
} 
} 
} 
Types that has flatMap implemented is a Monadic type (You can use flatMap 
to implement map)
Error Handling 
This can cause ArithmeticException 
def mean(xs: Seq[Double]): Double = 
xs.sum / xs.length 
Explicitly throw it, this is not pure (does not always return) 
def mean(xs: Seq[Double]): Double = 
if (xs.isEmpty) 
throw new ArithmeticException("mean of empty list!") 
else xs.sum / xs.length 
Have default number instead of throw exception,this is pure as it return Double 
all the time. But still bad for using a Double to represent empty List 
def mean_1(xs: IndexedSeq[Double], onEmpty: Double): Double = 
if (xs.isEmpty) onEmpty 
else xs.sum / xs.length 
All What else can we do?
Option/Try/Either 
sealed trait Option[+A] 
case object None extends Option[Nothing] 
case class Some[+A](get: A) extends Option[A] 
def mean_option(xs: Seq[Double]): Option[Double] = 
if (xs.isEmpty) None 
else Some(xs.sum / xs.length) 
sealed trait Try[+T] 
case class Failure[+T](exception: Throwable) extends Try[T] 
case class Success[+T](value: T) extends Try[T] 
def mean_try(xs: Seq[Double]): Try[Double] = 
if (xs.isEmpty) Failure(new ArithmeticException("mean of 
empty list!")) 
else Success(xs.sum / xs.length) 
sealed trait Either[+E,+A] 
case class Left[+E](get: E) extends Either[E,Nothing] 
case class Right[+A](get: A) extends Either[Nothing,A] 
def mean_either(xs: Seq[Double]): Either[String, Double] = 
if (xs.isEmpty) Left("Why gave me a empty Seq and ask me to 
get mean?") 
else Right(xs.sum / xs.length) 
Now we return a richer type that can represent 
correct value and a wrong value, Option represent 
wrong value as None (Nothing), Try represent with 
wrong value as Failure (Throwable), Either 
represent wrong value as some type you specified.
Option/Try/Either 
They all are monadic types, and have flatMap, 
map and filter defined with them (Either is not a 
monad, but left/right projection of Either are), so 
we can use the for comprehension. 
The error (the part that we do not want) will 
propagate in the chain of operations. One error 
means final result is error. 
def test = for { 
x <- mean_option(Seq(1,2,3,4,5)) 
y <- mean_option(Seq(4,5)) 
z <- mean_option(Seq()) 
} yield (x+y+z) 
None 
def test = for { 
x <- mean_either(Seq(1,2,3,4,5)).left 
y <- mean_either(Seq(4,5)).left 
z <- mean_either(Seq()).left 
} yield (x+y+z) 
Right(3.0) 
def test = for { 
x <- mean_either(Seq(1,2,3,4,5)).right 
y <- mean_either(Seq(4,5)).right 
z <- mean_either(Seq()).right 
} yield (x+y+z) 
Left(Why gave me a empty Seq and ask me 
to get mean?) 
def test1 = for { 
x <- mean_try(Seq(1,2,3,4,5)) 
y <- mean_try(Seq(4,5)) 
z <- mean_try(Seq()) 
} yield (x+y+z) 
Failure(java.lang. 
ArithmeticException: mean of empty 
list!)
Option/Try/Either 
Option, Try and Either all have much more 
combinators (functions that work on the them) 
give them even more tools to be useful 
You can see it in the links above.
Laziness / Strictness 
Generally speaking, laziness lets us separate 
the description of an expression from the 
evaluation of that expression. 
Strictness is just link the description of an 
expression and the evaluation of that 
expression together.
Stream 
We want to do this: 
List(1,2,3,4).map(_ + 10).filter(_ % 2 == 0).map(_ * 3) 
It is in fact: 
List(1,2,3,4).map(_ + 10).filter(_ % 2 == 0).map(_ * 3) 
List(11,12,13,14).filter(_ % 2 == 0).map(_ * 3) 
List(12,14).map(_ * 3) 
List(36,42) 
3 passes 
We can in fact use Laziness make it one pass
Examine Laziness 
false && { println("!!"); true } 
true || { println("!!"); false } 
if (input.isEmpty) sys.error("empty input") else input 
These are in fact all lazy evaluation (no prints) 
We can do the same: 
def if2[A](cond: Boolean, onTrue: => A, onFalse: => A): A = 
if (cond) onTrue else onFalse 
=> A here means lazy evaluate the passed in parameter, 
by not evaluating them until they are used. 
if2(false, sys.error("fail"), 3) 
get 3
Examine Laziness 
def maybeTwice(b: Boolean, i: => Int) = if (b) i+i else 0 
val x = maybeTwice(true, { println("hi"); 1+41 }) 
get 
hi 
hi 
84 
the above is because lazy Param evaluated every time,we can fix it by using 
this: 
def maybeTwice2(b: Boolean, i: => Int) = { 
lazy val j = i 
if (b) j+j else 0 
} 
val x = maybeTwice2(true, { println("hi"); 1+41 }) 
get 
hi 
84 
Because lazy val will cache the value
Stream (Lazy List) 
sealed abstract class Stream[+A] 
object Empty extends Stream[Nothing] 
sealed abstract class Cons[+A] extends Stream[A] 
def empty[A]: Stream[A] = Empty // A "smart constructor" for creating an empty stream of a particular type. 
def cons[A](hd: => A, tl: => Stream[A]): Stream[A] = new Cons[A] { // A "smart constructor" for creating a nonempty stream. 
lazy val head = hd // The head and tail are implemented by lazy vals. 
lazy val tail = tl 
} 
By carefully using lazy val and => in parameter list, we can 
achieve the lazy List: Stream, whose data member will not 
be evaluated before get used.
Stream 
Stream(1,2,3,4).map(_ + 10).filter(_ % 2 == 0).map(_ * 3) 
It is in fact: 
Stream(1,2,3,4).map(_ + 10).filter(_ % 2 == 0).map(_ * 3) 
cons(11,Stream(2,3,4).map(_ + 10)).filter(_ % 2 == 0).map(_ * 3) 
Stream(2,3,4).map(_ + 10).filter(_ % 2 == 0).map(_ * 3) 
cons(12,Stream(3,4).map(_ + 10)).filter(_ % 2 == 0).map(_ * 3) 
cons(12,Stream(3,4).map(_ + 10).filter(_ % 2 == 0)).map(_ * 3) 
cons(36,Stream(3,4).map(_ + 10).filter(_ % 2 == 0).map(_ * 3)) 
cons(36,cons(13, Stream(4).map(_ + 10)).filter(_ % 2 == 0).map(_ * 3)) 
…. 
cons(36,cons(42, Empty) 
Stream(36,42) 
1 pass
Stream 
def unfold[A, S](z: S)(f: S => Option[(A, S)]): Stream[A] = 
f(z) match { 
case Some((h,s)) => cons(h, unfold(s)(f)) 
case None => empty 
} 
unfold is a generic powerful 
combinator to generate infinite 
Stream. 
val fibsViaUnfold: Stream[Int] = cons(0, 
unfold((0,1)) { case (f0,f1) => Some((f1,(f1,f0+f1))) }) 
def test6 = fibsViaUnfold.take(5).toList 
get 
List(0, 1, 1, 2, 3) 
Stream is also a monadic type like 
List, and can use for comprehension: 
def test = { 
val a = Stream(1,2) 
val b = Stream(3,4) 
for { 
t1 <- a 
t2 <- b 
} yield ((t1,t2)) 
} 
get 
Stream((1,3), ?) 
? means it is lazy evaluated, you need 
to use toList/foreach to get the real 
value
Future 
trait Future[+T] 
when complete return a Try[T] 
trait Promise[T] 
It wraps in a future result of type T which can be a Failure 
It is closely related to Promise, Promise is one way to 
generate a Future. Promise generate a Future and later 
fulfill it.
Future/Promise 
val p = promise[Int] 
val f = p.future 
def producer = { 
println("Begin producer") 
Thread.sleep(3000) 
val r = 100 //produce an Int 
p success r 
println("end producer") 
} 
def consumer = { 
println("Begin consumer") 
f onSuccess { 
case r => println("receive product: " + r.toString) 
} 
println("end consumer") 
} 
def test2 = { 
consumer 
producer 
} 
get result as: 
Begin consumer 
end consumer 
Begin producer 
(after 3 secs) 
end producer 
receive product: 100
Future 
Future is also a monadic type, you can in fact do for 
comprehension on Futures. 
Basically, it will give you the ability to specify how to use 
the data inside the futures before it really have a value. 
If one of the value is a failure, it will automatically propagate 
downstream until you want to handle it case by case. 
If you have dependency between the futures (based on 
parameter chain), it will automatically wait.
Future 
def xFut: Future[Int] = future { 
Thread.sleep(10000); 
println("x happened"); 
10 
}.flatMap(i => Future.successful(i + 1)) 
def yFut(a: Int) : Future[Int] = future { 
println("y begin") 
Thread.sleep(6000); 
println("y happened " + a); 
20 
} 
def zFut(a: Int): Future[Int] = future { 
println("z begin") 
Thread.sleep(5000); 
println("z hapenned " + a); 
30 
} 
result is: 
(after 10 secs) x happened 
(immediately after) y begin 
(immediately after) z begin 
(after 5 secs) z hapenned 11 
(after 1 secs) y happened 11 
(after 4 secs) 
The end 
def test = { 
val xf = xFut 
val result: Future[(Int, (Int, Int))] = 
for { 
x <- xf 
a <- af(x) 
} yield (x, a) 
Thread.sleep(20000) 
println("nThe end") 
} 
def af: Int => Future[(Int, Int)] = a => 
{ 
val yf = yFut(a) 
val zf = zFut(a) 
for { 
y <- yf 
z <- zf 
} yield ((y,z)) 
}
External Effect 
def sideEffect(x: Int): Int = { 
println(x) 
x+1 
} 
This is a function has side effect, you can not really use its 
result to substitute it in code (no referential transparency) 
For a pure FP, you can not have this, what can you do?
IO monad 
wrap in the IO side effect and 
produces description of the IO 
effect (without executing), and 
then at the end, let some 
interpreter do the job based on 
the description. 
So you have a pure core, and a 
non pure shell for you program. 
trait IO[+A] { 
self => 
def run: A 
def map[B](f: A => B): IO[B] = 
new IO[B] { def run = f(self.run) } 
def flatMap[B](f: A => IO[B]): IO[B] = 
new IO[B] { def run = f(self.run).run } 
} 
object IO { 
def unit[A](a: => A): IO[A] = new IO[A] { def run = a } 
def flatMap[A,B](fa: IO[A])(f: A => IO[B]) = fa flatMap f 
def apply[A](a: => A): IO[A] = unit(a) 
}
IO monad in action 
def ReadLine: IO[String] = IO { readLine } 
def PrintLine(msg: String): IO[Unit] = IO { println(msg) } 
def fahrenheitToCelsius(f: Double): Double = 
(f - 32) * 5.0/9.0 
def converter: IO[Unit] = for { 
_ <- PrintLine("Enter a temperature in degrees fahrenheit: ") 
d <- ReadLine.map(_.toDouble) 
_ <- PrintLine(fahrenheitToCelsius(d).toString) 
} yield () 
get 
IO[Unit] = IO$$anon$2@2a9d61bf 
converter.run 
This will really run it.
IO monad 
Can be expanded to aggregate different 
source, distribute to different destination 
Can even expanded to Future like async 
operation.
Internal state 
def rollDie: Int = { 
val rng = new scala.util.Random 
rng.nextInt(6) 
} 
This is not pure (can return different value based on same 
input), and this is wrong (return 0 to 5), and hard to test 
(correct 5 out of 6 times) 
For a pure FP, you can not have this, what can you do?
State monad 
sealed trait State[S,A] { self => 
def run(s: S): (A,S) 
def map[B](f: A => B): State[S,B] = new State[S,B] { 
def run(s: S) = { 
val (a, s1) = self.run(s) 
(f(a), s1) 
} 
} 
def flatMap[B](f: A => State[S,B]): State[S,B] = new 
State[S,B] { 
def run(s: S) = { 
val (a, s1) = self.run(s) 
f(a).run(s1) 
} 
} 
} 
object State { 
def apply[S,A](f: S => (A,S) ) = { 
new State[S,A] { 
def run(s: S): (A,S) = f(s) 
} 
} 
def unit[S, A](a: A): State[S, A] = 
State(s => (a, s)) 
def get[S]: State[S, S] = State(s => (s, s)) 
def set[S](s: S): State[S, Unit] = State(_ => ((), s)) 
def modify[S](f: S => S): State[S, Unit] = for { 
s <- get 
_ <- set(f(s)) 
} yield () 
}
State monad 
basically, we wrap in the internal state S, 
transition take a state, produce a output and a 
new state. 
It will not run until you ask it run with a state. So 
it like IO monad, provide a description of the 
transition flow and delay the execution. Before 
execution, you can combine it in any way.
State monad in action 
trait RNG { 
def nextInt: (Int, RNG) 
} 
case class Simple(seed: Long) extends RNG{ 
def nextInt: (Int, RNG) = { 
val newSeed = (seed * 0x5DEECE66DL + 0xBL) & 0xFFFFFFFFFFFFL 
val nextRNG = Simple(newSeed) 
val n = (newSeed >>> 16).toInt 
(n, nextRNG) 
} 
} 
type Rand[A] = State[RNG, A] 
val int: Rand[Int] = State(_.nextInt) 
generate new random generators: 
val posInt = int.map[Int]{ i:Int => if (i < 0) -(i + 1) else i } 
def positiveLessThan(n: Int): Rand[Int] = posInt.flatMap { 
i => { 
val mod = i % n 
if (i + (n-1) - mod > 0) State.unit(mod) else positiveLessThan(n) 
}} 
positiveLessThan(6).run(Simple(5)) 
produce: 0 (now we can test it) 
chain of state transitions: produce 3 result from one initial state 
def test4 = 
for { 
x <- int 
y <- int 
z <- posInt 
} yield ((x , y , z)) 
test4.run(Simple(1)) produce ((384748,-1151252339,549383846), 
Simple(245470556921330))
State monad 
State monad can in fact be expanded to accept 
an input for transition, (A, S) => (B, S), a 
powerful State Machine 
State monad can also be expanded so that we 
can use the Scala static type system to check if 
any internal variable leaked outside (we can 
have local variable, but still immutable from 
outside)
for unit and flatMap 
Just to remind that although we used for comprehension for all examples, for comprehension is in fact 
just syntax sugar for unit/flatMap in Scala 
for { 
r1 <- m1 
r2 <- m2 
……. 
rx <- mx 
} yield (f(r1, r2, ….., rx)) 
is always the translated by compiler to 
m1.flatMap { 
r1 => m2.flatMap { 
r2 => m3.flatMap { 
…….. 
r x-1 => mx.flatMap { 
rx => unit(f(r1, r2, ….., rx)) 
} 
…….. 
}
Monad 
All the types we introduced in this session are all monadic 
types. 
So what is a monad: 
● an abstract construction in Category Theory 
● an abstract structure of can do a sequence of chained 
operations in FP 
● a higher kinded types (type of types) in Scala 
Let us see the formal definition
Monad 
// 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 Monad[F[_]] extends Functor[F] { 
def unit[A](a: => A): F[A] 
def flatMap[A,B](ma: F[A])(f: A => F[B]): F[B] 
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)
Higher Kinded Type 
[F[ _ ]] is called Higher Kinded Type in Scala, basically a type of types. 
Compare to normal [A], this basically says we have a type F that will be used in 
code, and F itself is a type that can use F[A], where A is a value type. 
Use the value constructor to make a comparison: 
proper first-order higher-order 
value 10 (x: Int) => x (f(Int => Int)) => f(10) 
type String List Monad
Monad Laws 
As a formal definition of a structure, Monad is 
basically a group of types that can implement 
unit/flatMap function that match the signature in 
the previous slide. 
But that is not enough, we also have law that 
Monad type need to fulfill so that we know our 
implementation of unit/flatMap is correct.
Monad Laws 
// 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) 
As the name suggest, associative law means flatMap 
operation obey the associative law similar to plus/multiple 
(a+b) + c = a + (b+c) 
As the name suggest, identity laws basically means we 
have a unit function that server as a identity in monad, like 
0 in addition, which similar to x + 0 = x, and 0 + x = x
Still, what is a Monad 
Now see all these formal definition, still what is a Monad for us programmer? and Why we need them? 
def unit[A](a: => A): M[A] 
def flatMap[A,B](ma: M[A])(f: A => M[B]): M[B] 
A bit long explanation here: 
Monad seems just like a wrapper, it wraps in a basic type (A here), and put into a context M, generate 
a richer type (M[A] here). unit function does this. 
We care about the value of type A in context M, but we hate part of the context that is troublesome. 
The troublesome part in the context M make us lose the composability for values of type A in 
context M (make us not be able to combine functions generate value of type A). So we wrap in the 
value and troublesome part together into context M, and now we can combine functions that generate 
M[A], just as if the troublesome part is gone. That is what flatMap does. 
Using unit and flatMap, we regain the composability for values of type A in context M, which is 
kind of what monad brings us, and it specifically useful in pure FP as side effect are the things prevent 
clean combination of functions.
Example monads in our talk 
Context Troublesome Part 
List multiple value 
Option can have empty value 
Try can have error 
Either can be another unintended (error message etc.) value 
Stream multiple value and also not accessible until touched 
Future can have error and also have latency to be availuable 
IO input/output side effect 
State internal states side effect

Fp in scala part 2

  • 1.
    FP in Scala the exploratorium for monads (part 2)
  • 2.
    for comprehension (review) Use the OO/FP hybrid style List code we can do def test = { val a = List(1,2) val b = List(3,4) for { t1 <- a t2 <- b } yield ((t1,t2)) } We get [ (1,3) (1,4) (2,3) (2,4) ]
  • 3.
    for comprehension (review) It is in fact flatMap/map in disguise, it is same as def test = { val a = List(1,2) val b = List(3,4) a.flatMap { t1 => b.map { t2 => (t1,t2) } } } Types that has flatMap implemented is a Monadic type (You can use flatMap to implement map)
  • 4.
    Error Handling Thiscan cause ArithmeticException def mean(xs: Seq[Double]): Double = xs.sum / xs.length Explicitly throw it, this is not pure (does not always return) def mean(xs: Seq[Double]): Double = if (xs.isEmpty) throw new ArithmeticException("mean of empty list!") else xs.sum / xs.length Have default number instead of throw exception,this is pure as it return Double all the time. But still bad for using a Double to represent empty List def mean_1(xs: IndexedSeq[Double], onEmpty: Double): Double = if (xs.isEmpty) onEmpty else xs.sum / xs.length All What else can we do?
  • 5.
    Option/Try/Either sealed traitOption[+A] case object None extends Option[Nothing] case class Some[+A](get: A) extends Option[A] def mean_option(xs: Seq[Double]): Option[Double] = if (xs.isEmpty) None else Some(xs.sum / xs.length) sealed trait Try[+T] case class Failure[+T](exception: Throwable) extends Try[T] case class Success[+T](value: T) extends Try[T] def mean_try(xs: Seq[Double]): Try[Double] = if (xs.isEmpty) Failure(new ArithmeticException("mean of empty list!")) else Success(xs.sum / xs.length) sealed trait Either[+E,+A] case class Left[+E](get: E) extends Either[E,Nothing] case class Right[+A](get: A) extends Either[Nothing,A] def mean_either(xs: Seq[Double]): Either[String, Double] = if (xs.isEmpty) Left("Why gave me a empty Seq and ask me to get mean?") else Right(xs.sum / xs.length) Now we return a richer type that can represent correct value and a wrong value, Option represent wrong value as None (Nothing), Try represent with wrong value as Failure (Throwable), Either represent wrong value as some type you specified.
  • 6.
    Option/Try/Either They allare monadic types, and have flatMap, map and filter defined with them (Either is not a monad, but left/right projection of Either are), so we can use the for comprehension. The error (the part that we do not want) will propagate in the chain of operations. One error means final result is error. def test = for { x <- mean_option(Seq(1,2,3,4,5)) y <- mean_option(Seq(4,5)) z <- mean_option(Seq()) } yield (x+y+z) None def test = for { x <- mean_either(Seq(1,2,3,4,5)).left y <- mean_either(Seq(4,5)).left z <- mean_either(Seq()).left } yield (x+y+z) Right(3.0) def test = for { x <- mean_either(Seq(1,2,3,4,5)).right y <- mean_either(Seq(4,5)).right z <- mean_either(Seq()).right } yield (x+y+z) Left(Why gave me a empty Seq and ask me to get mean?) def test1 = for { x <- mean_try(Seq(1,2,3,4,5)) y <- mean_try(Seq(4,5)) z <- mean_try(Seq()) } yield (x+y+z) Failure(java.lang. ArithmeticException: mean of empty list!)
  • 7.
    Option/Try/Either Option, Tryand Either all have much more combinators (functions that work on the them) give them even more tools to be useful You can see it in the links above.
  • 8.
    Laziness / Strictness Generally speaking, laziness lets us separate the description of an expression from the evaluation of that expression. Strictness is just link the description of an expression and the evaluation of that expression together.
  • 9.
    Stream We wantto do this: List(1,2,3,4).map(_ + 10).filter(_ % 2 == 0).map(_ * 3) It is in fact: List(1,2,3,4).map(_ + 10).filter(_ % 2 == 0).map(_ * 3) List(11,12,13,14).filter(_ % 2 == 0).map(_ * 3) List(12,14).map(_ * 3) List(36,42) 3 passes We can in fact use Laziness make it one pass
  • 10.
    Examine Laziness false&& { println("!!"); true } true || { println("!!"); false } if (input.isEmpty) sys.error("empty input") else input These are in fact all lazy evaluation (no prints) We can do the same: def if2[A](cond: Boolean, onTrue: => A, onFalse: => A): A = if (cond) onTrue else onFalse => A here means lazy evaluate the passed in parameter, by not evaluating them until they are used. if2(false, sys.error("fail"), 3) get 3
  • 11.
    Examine Laziness defmaybeTwice(b: Boolean, i: => Int) = if (b) i+i else 0 val x = maybeTwice(true, { println("hi"); 1+41 }) get hi hi 84 the above is because lazy Param evaluated every time,we can fix it by using this: def maybeTwice2(b: Boolean, i: => Int) = { lazy val j = i if (b) j+j else 0 } val x = maybeTwice2(true, { println("hi"); 1+41 }) get hi 84 Because lazy val will cache the value
  • 12.
    Stream (Lazy List) sealed abstract class Stream[+A] object Empty extends Stream[Nothing] sealed abstract class Cons[+A] extends Stream[A] def empty[A]: Stream[A] = Empty // A "smart constructor" for creating an empty stream of a particular type. def cons[A](hd: => A, tl: => Stream[A]): Stream[A] = new Cons[A] { // A "smart constructor" for creating a nonempty stream. lazy val head = hd // The head and tail are implemented by lazy vals. lazy val tail = tl } By carefully using lazy val and => in parameter list, we can achieve the lazy List: Stream, whose data member will not be evaluated before get used.
  • 13.
    Stream Stream(1,2,3,4).map(_ +10).filter(_ % 2 == 0).map(_ * 3) It is in fact: Stream(1,2,3,4).map(_ + 10).filter(_ % 2 == 0).map(_ * 3) cons(11,Stream(2,3,4).map(_ + 10)).filter(_ % 2 == 0).map(_ * 3) Stream(2,3,4).map(_ + 10).filter(_ % 2 == 0).map(_ * 3) cons(12,Stream(3,4).map(_ + 10)).filter(_ % 2 == 0).map(_ * 3) cons(12,Stream(3,4).map(_ + 10).filter(_ % 2 == 0)).map(_ * 3) cons(36,Stream(3,4).map(_ + 10).filter(_ % 2 == 0).map(_ * 3)) cons(36,cons(13, Stream(4).map(_ + 10)).filter(_ % 2 == 0).map(_ * 3)) …. cons(36,cons(42, Empty) Stream(36,42) 1 pass
  • 14.
    Stream def unfold[A,S](z: S)(f: S => Option[(A, S)]): Stream[A] = f(z) match { case Some((h,s)) => cons(h, unfold(s)(f)) case None => empty } unfold is a generic powerful combinator to generate infinite Stream. val fibsViaUnfold: Stream[Int] = cons(0, unfold((0,1)) { case (f0,f1) => Some((f1,(f1,f0+f1))) }) def test6 = fibsViaUnfold.take(5).toList get List(0, 1, 1, 2, 3) Stream is also a monadic type like List, and can use for comprehension: def test = { val a = Stream(1,2) val b = Stream(3,4) for { t1 <- a t2 <- b } yield ((t1,t2)) } get Stream((1,3), ?) ? means it is lazy evaluated, you need to use toList/foreach to get the real value
  • 15.
    Future trait Future[+T] when complete return a Try[T] trait Promise[T] It wraps in a future result of type T which can be a Failure It is closely related to Promise, Promise is one way to generate a Future. Promise generate a Future and later fulfill it.
  • 16.
    Future/Promise val p= promise[Int] val f = p.future def producer = { println("Begin producer") Thread.sleep(3000) val r = 100 //produce an Int p success r println("end producer") } def consumer = { println("Begin consumer") f onSuccess { case r => println("receive product: " + r.toString) } println("end consumer") } def test2 = { consumer producer } get result as: Begin consumer end consumer Begin producer (after 3 secs) end producer receive product: 100
  • 17.
    Future Future isalso a monadic type, you can in fact do for comprehension on Futures. Basically, it will give you the ability to specify how to use the data inside the futures before it really have a value. If one of the value is a failure, it will automatically propagate downstream until you want to handle it case by case. If you have dependency between the futures (based on parameter chain), it will automatically wait.
  • 18.
    Future def xFut:Future[Int] = future { Thread.sleep(10000); println("x happened"); 10 }.flatMap(i => Future.successful(i + 1)) def yFut(a: Int) : Future[Int] = future { println("y begin") Thread.sleep(6000); println("y happened " + a); 20 } def zFut(a: Int): Future[Int] = future { println("z begin") Thread.sleep(5000); println("z hapenned " + a); 30 } result is: (after 10 secs) x happened (immediately after) y begin (immediately after) z begin (after 5 secs) z hapenned 11 (after 1 secs) y happened 11 (after 4 secs) The end def test = { val xf = xFut val result: Future[(Int, (Int, Int))] = for { x <- xf a <- af(x) } yield (x, a) Thread.sleep(20000) println("nThe end") } def af: Int => Future[(Int, Int)] = a => { val yf = yFut(a) val zf = zFut(a) for { y <- yf z <- zf } yield ((y,z)) }
  • 19.
    External Effect defsideEffect(x: Int): Int = { println(x) x+1 } This is a function has side effect, you can not really use its result to substitute it in code (no referential transparency) For a pure FP, you can not have this, what can you do?
  • 20.
    IO monad wrapin the IO side effect and produces description of the IO effect (without executing), and then at the end, let some interpreter do the job based on the description. So you have a pure core, and a non pure shell for you program. trait IO[+A] { self => def run: A def map[B](f: A => B): IO[B] = new IO[B] { def run = f(self.run) } def flatMap[B](f: A => IO[B]): IO[B] = new IO[B] { def run = f(self.run).run } } object IO { def unit[A](a: => A): IO[A] = new IO[A] { def run = a } def flatMap[A,B](fa: IO[A])(f: A => IO[B]) = fa flatMap f def apply[A](a: => A): IO[A] = unit(a) }
  • 21.
    IO monad inaction def ReadLine: IO[String] = IO { readLine } def PrintLine(msg: String): IO[Unit] = IO { println(msg) } def fahrenheitToCelsius(f: Double): Double = (f - 32) * 5.0/9.0 def converter: IO[Unit] = for { _ <- PrintLine("Enter a temperature in degrees fahrenheit: ") d <- ReadLine.map(_.toDouble) _ <- PrintLine(fahrenheitToCelsius(d).toString) } yield () get IO[Unit] = IO$$anon$2@2a9d61bf converter.run This will really run it.
  • 22.
    IO monad Canbe expanded to aggregate different source, distribute to different destination Can even expanded to Future like async operation.
  • 23.
    Internal state defrollDie: Int = { val rng = new scala.util.Random rng.nextInt(6) } This is not pure (can return different value based on same input), and this is wrong (return 0 to 5), and hard to test (correct 5 out of 6 times) For a pure FP, you can not have this, what can you do?
  • 24.
    State monad sealedtrait State[S,A] { self => def run(s: S): (A,S) def map[B](f: A => B): State[S,B] = new State[S,B] { def run(s: S) = { val (a, s1) = self.run(s) (f(a), s1) } } def flatMap[B](f: A => State[S,B]): State[S,B] = new State[S,B] { def run(s: S) = { val (a, s1) = self.run(s) f(a).run(s1) } } } object State { def apply[S,A](f: S => (A,S) ) = { new State[S,A] { def run(s: S): (A,S) = f(s) } } def unit[S, A](a: A): State[S, A] = State(s => (a, s)) def get[S]: State[S, S] = State(s => (s, s)) def set[S](s: S): State[S, Unit] = State(_ => ((), s)) def modify[S](f: S => S): State[S, Unit] = for { s <- get _ <- set(f(s)) } yield () }
  • 25.
    State monad basically,we wrap in the internal state S, transition take a state, produce a output and a new state. It will not run until you ask it run with a state. So it like IO monad, provide a description of the transition flow and delay the execution. Before execution, you can combine it in any way.
  • 26.
    State monad inaction trait RNG { def nextInt: (Int, RNG) } case class Simple(seed: Long) extends RNG{ def nextInt: (Int, RNG) = { val newSeed = (seed * 0x5DEECE66DL + 0xBL) & 0xFFFFFFFFFFFFL val nextRNG = Simple(newSeed) val n = (newSeed >>> 16).toInt (n, nextRNG) } } type Rand[A] = State[RNG, A] val int: Rand[Int] = State(_.nextInt) generate new random generators: val posInt = int.map[Int]{ i:Int => if (i < 0) -(i + 1) else i } def positiveLessThan(n: Int): Rand[Int] = posInt.flatMap { i => { val mod = i % n if (i + (n-1) - mod > 0) State.unit(mod) else positiveLessThan(n) }} positiveLessThan(6).run(Simple(5)) produce: 0 (now we can test it) chain of state transitions: produce 3 result from one initial state def test4 = for { x <- int y <- int z <- posInt } yield ((x , y , z)) test4.run(Simple(1)) produce ((384748,-1151252339,549383846), Simple(245470556921330))
  • 27.
    State monad Statemonad can in fact be expanded to accept an input for transition, (A, S) => (B, S), a powerful State Machine State monad can also be expanded so that we can use the Scala static type system to check if any internal variable leaked outside (we can have local variable, but still immutable from outside)
  • 28.
    for unit andflatMap Just to remind that although we used for comprehension for all examples, for comprehension is in fact just syntax sugar for unit/flatMap in Scala for { r1 <- m1 r2 <- m2 ……. rx <- mx } yield (f(r1, r2, ….., rx)) is always the translated by compiler to m1.flatMap { r1 => m2.flatMap { r2 => m3.flatMap { …….. r x-1 => mx.flatMap { rx => unit(f(r1, r2, ….., rx)) } …….. }
  • 29.
    Monad All thetypes we introduced in this session are all monadic types. So what is a monad: ● an abstract construction in Category Theory ● an abstract structure of can do a sequence of chained operations in FP ● a higher kinded types (type of types) in Scala Let us see the formal definition
  • 30.
    Monad // idfunction: // 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 Monad[F[_]] extends Functor[F] { def unit[A](a: => A): F[A] def flatMap[A,B](ma: F[A])(f: A => F[B]): F[B] 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)
  • 31.
    Higher Kinded Type [F[ _ ]] is called Higher Kinded Type in Scala, basically a type of types. Compare to normal [A], this basically says we have a type F that will be used in code, and F itself is a type that can use F[A], where A is a value type. Use the value constructor to make a comparison: proper first-order higher-order value 10 (x: Int) => x (f(Int => Int)) => f(10) type String List Monad
  • 32.
    Monad Laws Asa formal definition of a structure, Monad is basically a group of types that can implement unit/flatMap function that match the signature in the previous slide. But that is not enough, we also have law that Monad type need to fulfill so that we know our implementation of unit/flatMap is correct.
  • 33.
    Monad Laws //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) As the name suggest, associative law means flatMap operation obey the associative law similar to plus/multiple (a+b) + c = a + (b+c) As the name suggest, identity laws basically means we have a unit function that server as a identity in monad, like 0 in addition, which similar to x + 0 = x, and 0 + x = x
  • 34.
    Still, what isa Monad Now see all these formal definition, still what is a Monad for us programmer? and Why we need them? def unit[A](a: => A): M[A] def flatMap[A,B](ma: M[A])(f: A => M[B]): M[B] A bit long explanation here: Monad seems just like a wrapper, it wraps in a basic type (A here), and put into a context M, generate a richer type (M[A] here). unit function does this. We care about the value of type A in context M, but we hate part of the context that is troublesome. The troublesome part in the context M make us lose the composability for values of type A in context M (make us not be able to combine functions generate value of type A). So we wrap in the value and troublesome part together into context M, and now we can combine functions that generate M[A], just as if the troublesome part is gone. That is what flatMap does. Using unit and flatMap, we regain the composability for values of type A in context M, which is kind of what monad brings us, and it specifically useful in pure FP as side effect are the things prevent clean combination of functions.
  • 35.
    Example monads inour talk Context Troublesome Part List multiple value Option can have empty value Try can have error Either can be another unintended (error message etc.) value Stream multiple value and also not accessible until touched Future can have error and also have latency to be availuable IO input/output side effect State internal states side effect