Functional Programming in
Scala
class Cafe {
def buyCoffee(cc: CreditCard): Coffee = {
val cup = Coffee(100)
cc.charge(100)
cup
}
}
case class Payments() {
def charge(cc: CreditCard, price: Int) {
println("doing something")
}
}
case class BetterCafe() {
...
case class Charge(cc: CreditCard, amount: Int) {
def combine(other: Charge): Charge = {
if (cc == other.cc) {
Charge(this....
class FunctionalCafe {
def buyCoffee(cc: CreditCard): (Coffee, Charge) = {
val cup = new Coffee(100)
(cup, Charge(cc, cup....
Pure Functions:
Reassigning a variable
Modifying a data structure in place
Setting a field on an object
Throwing an except...
Referential transparency
scala> val x = "Hello, World"
x: java.lang.String = Hello, World
scala> val r1 = x.reverse
r1: St...
scala> val x = new StringBuilder("Hello")
x: java.lang.StringBuilder = Hello
scala> val y = x.append(", World")
y: java.la...
scala> val x = new StringBuilder("Hello")
x: java.lang.StringBuilder = Hello
scala> val r1 = x.append(", World").toString(...
def factorial(n: Int): Int = {
if (n <= 0) 1
else n * factorial(n - 1)
}
def factorial(n: Int): Int = {
def go(n: Int, acc...
def incBy(x: Int): Int = {
@annotation.tailrec
def go(x: Int, acc: Int): Int =
if (x <= 0) acc
else go(x - 1, acc + 1)
go(...
High Order Functions:
def highOrder(a: Int, f: Int => Int): Int = {
f(a)
}
highOrder(10, ((x: Int) => x + 1))
highOrder(10...
def partial1[A, B, C](f: (A, B) => C, a: A): B => C = {
(b: B) => f(a, b)
}
def curry[A,B,C](f: (A, B) => C): A => (B => C...
Algebraic data type:
trait Weekday
case class Monday extends Weekday
case class Tuesday extends Weekday
case class Wednesd...
Immutable Linked List:
sealed trait List[+A]
case object Nil extends List[Nothing]
case class Cons[+A](head: A, tail: List[A])
extends List[A]
ob...
def sum(xs: List[Int]): Int = xs match {
case Nil
=> 0
case Cons(x, xs) => x + sum(xs)
}
def product(xs: List[Int]): Int =...
def tail[A](xs: List[A]): List[A] = xs match {
case Nil
=> throw new Exception("aaaa")
case Cons(x, xs) => xs
}
def setHea...
def dropWhile[A](xs: List[A], p: A => Boolean): List[A]
= xs match {
case Nil
=> Nil
case Cons(y, ys) if p(y) => dropWhile...
List Folding:
def foldRight[A, B](xs: List[A], acc: B)(f: (A, B) => B): B =
xs match {
case Nil
=> acc
case Cons(x, xs) =>...
def foldLeft[A, B](xs: List[A], acc: B)(f: (B, A) => B): B =
xs match {
case Nil
=> acc
case Cons(x, xs) => foldLeft(xs, f...
def length[A](xs: List[A]): Int =
foldRight(xs, 0)((x, acc) => acc + 1)
def sum(xs: List[Int]): Int =
foldLeft(xs, 0)(_ + ...
def foldLeftFR[A, B](l: List[A], z: B)(f: (B, A) => B): B =
foldRight(l, (b: B) => b)((a, g) => b => g(f(b, a)))(z)
def ap...
def unit[A](a: A): List[A] = Cons(a, Nil)
def map[A, B](xs: List[A])(f: A => B): List[B] = xs match {
case Nil
=> Nil
case...
def map [A](xs: List[A])(f: A => B): List[B] =
flatMap(xs)((a: A) => unit(f(a)))
def filter[A](xs: List[A])(p: A => Boolea...
Simple Binary Tree:
sealed trait Tree[+A]
case class Leaf[A](value: A) extends Tree[A]
case class Branch[A](left: Tree[A],...
def size[A](tree: Tree[A]): Int = tree match {
case Leaf(_)
=> 1
case Branch(l, r) => 1 + size(l) + size(r)
}
def maximum(...
def map[A, B](tree: Tree[A])(f: A => B): Tree[B] =
tree match {
case Leaf(x)
=> Leaf(f(x))
case Branch(l, r) => Branch(map...
How to deal with non total functions:
String countryName(String userId) {
User user;
Phone phone;
String cc;
Country count...
String countryName(User user) {
try {
return Countries.findByCode(
PSTN.extractCountryCode(user.getPhone()));
}
catch (Exc...
Option(Maybe):
case class Some[+A](get: A) extends Option[A]
case object None extends Option[Nothing]
def getOrElse[B >: A...
def map[B](f: A => B): Option[B] = this match {
case None
=> None
case Some(x) => Some(f(x))
}
def flatMap[B](f: A => Opti...
def f(v: Some[Int]): Some[Int] =
v.map(x => x + 1).map(x => x + 2).filter(x => x != 3)
val dept: String = employeesByName....
for {
x <- Some(10)
y <- Some(20)
} yield(x + y)

for (
user <- db.findUser(userid)
phone <- user.getPhone
cc <- phone.get...
Simple pseudo-random generator:
trait RNG {
def nextInt: (Int, RNG)
}
object RNG {
case class Simple(seed: Long) extends R...
def positiveInt(rng: RNG): (Int, RNG) = {
val (i, r) = rng.nextInt
if (i < 0) (-(i + 1), r)
else (i, r)
}
def double(rng: ...
def unit[A](a: A): Rand[A] =
rng => (a, rng)
def map[A, B](s: Rand[A])(f: A => B): Rand[B] = {
rng => {
val (v, r) = s(rng...
def booleanMap: Rand[Boolean] =
map(positiveInt)(x => if (x % 2 == 0) true else false)
def doubleMap: Rand[Double] =
map(p...
trait Functor[F[_]] {
def map[A, B](fa: F[A])(f: A => B): F[B]
def distribute[A, B](fab: F[(A, B)]): (F[A], F[B]) =
(map(f...
A monad is an implementation of one of the minimal sets of
monadic combinators, satisfying the laws of associativity and
i...
Monad laws:
x.flatMap(f).flatMap(g) ==
x.flatMap(a => f(a).flatMap(g))
unit(x) flatMap f == f(x)
flatMap(m)(unit) == m
References
https://www.coursera.org/course/progfun
https://www.coursera.org/course/reactive
http://www.manning.com/bjarnas...
Functional programming in scala
Functional programming in scala
Upcoming SlideShare
Loading in …5
×

Functional programming in scala

653 views

Published on

0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
653
On SlideShare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
12
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Functional programming in scala

  1. 1. Functional Programming in Scala
  2. 2. class Cafe { def buyCoffee(cc: CreditCard): Coffee = { val cup = Coffee(100) cc.charge(100) cup } }
  3. 3. case class Payments() { def charge(cc: CreditCard, price: Int) { println("doing something") } } case class BetterCafe() { def buyCoffee(cc: CreditCard, p: Payments): Coffee = { val cup = Coffee(100) p.charge(cc, cup.price) cup } }
  4. 4. case class Charge(cc: CreditCard, amount: Int) { def combine(other: Charge): Charge = { if (cc == other.cc) { Charge(this.cc, this.amount + other.amount) } else { throw new Exception("Error") } } }
  5. 5. class FunctionalCafe { def buyCoffee(cc: CreditCard): (Coffee, Charge) = { val cup = new Coffee(100) (cup, Charge(cc, cup.price)) } def buyCoffees(cc: CreditCard, number: Int): (List[Coffee], Charge) = { val purchases: List[(Coffee, Charge)] = List.fill(number)(buyCoffee(cc)) val (coffees, charges) = purchases.unzip (coffees, charges.reduce((a, b) => a.combine(b)) } }
  6. 6. Pure Functions: Reassigning a variable Modifying a data structure in place Setting a field on an object Throwing an exception or halting with an error* Printing to the console or reading user input Reading from or writing to a file Drawing on the screen
  7. 7. Referential transparency scala> val x = "Hello, World" x: java.lang.String = Hello, World scala> val r1 = x.reverse r1: String = dlroW ,olleH scala> val r2 = x.reverse r2: String = dlroW ,olleH scala> val r1 = "Hello, World".reverse r1: String = dlroW ,olleH val r2 = "Hello, World".reverse r2: String = dlroW ,olleH
  8. 8. scala> val x = new StringBuilder("Hello") x: java.lang.StringBuilder = Hello scala> val y = x.append(", World") y: java.lang.StringBuilder = Hello, World scala> val r1 = y.toString r1: java.lang.String = Hello, World scala> val r2 = y.toString r2: java.lang.String = Hello, World
  9. 9. scala> val x = new StringBuilder("Hello") x: java.lang.StringBuilder = Hello scala> val r1 = x.append(", World").toString() r1: java.lang.String = Hello, World scala> val r2 = x.append(", World").toString() r2: java.lang.String = Hello, World, World
  10. 10. def factorial(n: Int): Int = { if (n <= 0) 1 else n * factorial(n - 1) } def factorial(n: Int): Int = { def go(n: Int, acc: Int): Int = if (n <= 0) acc else go(n - 1, n * acc) go(n, 1) }
  11. 11. def incBy(x: Int): Int = { @annotation.tailrec def go(x: Int, acc: Int): Int = if (x <= 0) acc else go(x - 1, acc + 1) go(x, 1) }
  12. 12. High Order Functions: def highOrder(a: Int, f: Int => Int): Int = { f(a) } highOrder(10, ((x: Int) => x + 1)) highOrder(10, (x => x + 1) def isSorted[A](as: Array[A], gt: (A, A) => Boolean): Boolean = { @annotation.tailrec def go(i: Int, prev: A): Boolean = if (i == as.length) true else if (gt(as(i), prev)) go(i + 1, as(i)) else false if (as.length == 0) true else go(1, as(0)) }
  13. 13. def partial1[A, B, C](f: (A, B) => C, a: A): B => C = { (b: B) => f(a, b) } def curry[A,B,C](f: (A, B) => C): A => (B => C) = { (a: A) => (b: B) => f(a, b) } def uncurry[A, B, C](f: A => B => C): (A, B) => C = { (a: A, b: B) => f(a)(b) } def compose[A, B, C](f: A => B, g: B => C): A => C = { (a: A) => g(f(a)) }
  14. 14. Algebraic data type: trait Weekday case class Monday extends Weekday case class Tuesday extends Weekday case class Wednesday extends Weekday case class Thursday extends Weekday case class Friday extends Weekday case class Saturday extends Weekday case class Sunday extends Weekday trait Boolean case class True case class False
  15. 15. Immutable Linked List:
  16. 16. sealed trait List[+A] case object Nil extends List[Nothing] case class Cons[+A](head: A, tail: List[A]) extends List[A] object List { def apply[A](as: A*): List[A] = if (as.isEmpty) Nil else Cons(as.head, apply(as.tail: _*)) }
  17. 17. def sum(xs: List[Int]): Int = xs match { case Nil => 0 case Cons(x, xs) => x + sum(xs) } def product(xs: List[Int]): Int = xs match { case Nil => 1 case Cons(0, xs) => 0 case Cons(x, xs) => x * product(xs) }
  18. 18. def tail[A](xs: List[A]): List[A] = xs match { case Nil => throw new Exception("aaaa") case Cons(x, xs) => xs } def setHead[A](xs: List[A], a: A): List[A] = xs match { case Nil => Cons(a, Nil) case Cons(x, xs) => Cons(a, xs) } def drop[A](xs: List[A], n: Int): List[A] = (xs, n) match { case (Nil, _) => xs case (Cons(y, ys), 0) => xs case (Cons(y, ys), n) => drop(ys, n - 1) }
  19. 19. def dropWhile[A](xs: List[A], p: A => Boolean): List[A] = xs match { case Nil => Nil case Cons(y, ys) if p(y) => dropWhile(ys, p) case Cons(y, ys) => xs } def init[A](xs: List[A]): List[A] = xs match { case Nil => Nil case Cons(x, Nil) => Nil case Cons(x, xs) => Cons(x, init(xs)) }
  20. 20. List Folding: def foldRight[A, B](xs: List[A], acc: B)(f: (A, B) => B): B = xs match { case Nil => acc case Cons(x, xs) => f(x, foldRight(xs, acc)(f)) } foldRight(Cons(1, Cons(2, Cons(3, Nil))), 0)((x,y) => x + y) 1 + foldRight(Cons(2, Cons(3, Nil)), 0)((x,y) => x + y) 1 + (2 + foldRight(Cons(3, Nil), 0)((x,y) => x + y)) 1 + (2 + (3 + (foldRight(Nil, 0)((x,y) => x + y)))) 1 + (2 + (3 + (0))) 6
  21. 21. def foldLeft[A, B](xs: List[A], acc: B)(f: (B, A) => B): B = xs match { case Nil => acc case Cons(x, xs) => foldLeft(xs, f(acc, x))(f) } foldLeft(Cons(1, Cons(2, Cons(3, Nil))), 0)((x,y) => x + y) foldLeft(Cons(2, Cons(3, Nil)), 0 + 1)((x,y) => x + y) foldLeft(Cons(3, Nil)), 1 + 2)((x,y) => x + y) foldLeft(Nil, 3 + 3)((x,y) => x + y) 6
  22. 22. def length[A](xs: List[A]): Int = foldRight(xs, 0)((x, acc) => acc + 1) def sum(xs: List[Int]): Int = foldLeft(xs, 0)(_ + _) def product(xs: List[Int]): Int = foldLeft(xs, 1)(_ * _) def reverse[A](xs: List[A]): List[A] = foldLeft(xs, Nil: List[A])( (x: List[A], y: A) => Cons(y, x))
  23. 23. def foldLeftFR[A, B](l: List[A], z: B)(f: (B, A) => B): B = foldRight(l, (b: B) => b)((a, g) => b => g(f(b, a)))(z) def append[A](xs: List[A], ys: List[A]) = foldRight(xs, ys)((x, y) => Cons(x, y)) def inc(xs: List[Int]): List[Int] = foldLeft(reverse(xs), Nil: List[Int])( (x, y) => Cons(y + 1, x))
  24. 24. def unit[A](a: A): List[A] = Cons(a, Nil) def map[A, B](xs: List[A])(f: A => B): List[B] = xs match { case Nil => Nil case Cons(x, xs) => Cons(f(x), map(xs)(f)) } def flatMap[A, B](xs: List[A])(f: A => List[B]): List[B] = xs match { case Nil => Nil case Cons(x, xs) => append(f(x), (flatMap(xs)(f))) } def filter[A](xs: List[A])(p: A => Boolean): List[A] = xs match { case Nil => Nil case Cons(x, xs) if (p(x)) => Cons(x, filter(xs)(p)) case Cons(x, xs) => filter(xs)(p) }
  25. 25. def map [A](xs: List[A])(f: A => B): List[B] = flatMap(xs)((a: A) => unit(f(a))) def filter[A](xs: List[A])(p: A => Boolean): List[A] = flatMap(xs)(x => if (p(x)) List(x) else Nil)
  26. 26. Simple Binary Tree: sealed trait Tree[+A] case class Leaf[A](value: A) extends Tree[A] case class Branch[A](left: Tree[A], right: Tree[A]) extends Tree[A]
  27. 27. def size[A](tree: Tree[A]): Int = tree match { case Leaf(_) => 1 case Branch(l, r) => 1 + size(l) + size(r) } def maximum(tree: Tree[Int]): Int = tree match { case Leaf(x) => x case Branch(l, r) => maximum(l) max maximum(r) } def depth[A](tree: Tree[A]): Int = tree match { case Leaf(_) => 1 case Branch(l, r) => 1 + (depth(l) max depth(r)) }
  28. 28. def map[A, B](tree: Tree[A])(f: A => B): Tree[B] = tree match { case Leaf(x) => Leaf(f(x)) case Branch(l, r) => Branch(map(l)(f), map(r)(f)) } def fold[A, B](tree: Tree[A])(f: A => B)(g: (B, B) => B): B = tree match { case Leaf(x) => f(x) case Branch(l, r) => g(fold(l)(f)(g), fold(r)(f)(g)) } def sizeViaFold[A](tree: Tree[A]): Int = fold(tree)((a: A) => 1)((b1: Int, b2: Int) => 1 + b1 + b2) def mapViaFold[A, B](tree: Tree[A])(f: A => B): Tree[B] = fold(tree)(a => Leaf(f(a)): Tree[B]) ((l, r) => Branch(l, r))
  29. 29. How to deal with non total functions: String countryName(String userId) { User user; Phone phone; String cc; Country country; return userid != null && (user = db.findUser(userid)) != null && (phone = user.getPhone) != null && (cc = phone.getCountryCode) != null && (country = Countries.findByCode(cc)) != null ? country.getName() : null }
  30. 30. String countryName(User user) { try { return Countries.findByCode( PSTN.extractCountryCode(user.getPhone())); } catch (Exception npe) { return null; } }
  31. 31. Option(Maybe): case class Some[+A](get: A) extends Option[A] case object None extends Option[Nothing] def getOrElse[B >: A](default: => B): B = this match { case None => default case Some(x) => x } def unit[A](a: A): Option[A] = Some(a) val a = unit(10) val value = a match { case None => “exception” case Some(x) => x } val c = a + 10
  32. 32. def map[B](f: A => B): Option[B] = this match { case None => None case Some(x) => Some(f(x)) } def flatMap[B](f: A => Option[B]): Option[B] = this match { case None => None case Some(x) => f(x) } def filter(p: A => Boolean): Option[A] = this match { case Some(x) if (p(x)) => this case _ => None }
  33. 33. def f(v: Some[Int]): Some[Int] = v.map(x => x + 1).map(x => x + 2).filter(x => x != 3) val dept: String = employeesByName.get("Joe"). map(_.dept). filter(_ != "Accounting"). getOrElse("Default Dept") val a = Some(10) val b = Some(20) a + b ? a.map(… a.flatMap(x => b.map(y => x + y))
  34. 34. for { x <- Some(10) y <- Some(20) } yield(x + y) for ( user <- db.findUser(userid) phone <- user.getPhone cc <- phone.getCountryCode country <- Countries.findByCode(cc) ) yield("User " + user + " is from " + country)
  35. 35. Simple pseudo-random generator: trait RNG { def nextInt: (Int, RNG) } object 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] = RNG => (A, RNG) val int: Rand[Int] = (x => x.nextInt)
  36. 36. def positiveInt(rng: RNG): (Int, RNG) = { val (i, r) = rng.nextInt if (i < 0) (-(i + 1), r) else (i, r) } def double(rng: RNG): (Double, RNG) = { val (i, r) = positiveInt(rng) if (i == Int.MaxValue) double(r) else (i.toDouble / Int.MaxValue, r) }
  37. 37. def unit[A](a: A): Rand[A] = rng => (a, rng) def map[A, B](s: Rand[A])(f: A => B): Rand[B] = { rng => { val (v, r) = s(rng) (f(v), r) } } def flatMap[A,B](f: Rand[A])(g: A => Rand[B]): Rand[B] = { rng => { val (v, r) = f(rng) g(v)(r) } }
  38. 38. def booleanMap: Rand[Boolean] = map(positiveInt)(x => if (x % 2 == 0) true else false) def doubleMap: Rand[Double] = map(positiveInt)(x => x / Int.MaxValue.toDouble + 1) val g = Rand[(Int, Boolean, Double)] = for { x <- int y <- booleanMap z <- doubleMap } yield (x, y, z)
  39. 39. trait Functor[F[_]] { def map[A, B](fa: F[A])(f: A => B): F[B] def distribute[A, B](fab: F[(A, B)]): (F[A], F[B]) = (map(fab)(x => x._1), map(fab)(x => x._2)) } val listFunctor = new Functor[List] { def map[A, B](fa: List[A])(f: A => B): List[B] = fa.map(f) } Functor law: map(x)(id) == x
  40. 40. A monad is an implementation of one of the minimal sets of monadic combinators, satisfying the laws of associativity and identity. unit and flatMap unit and compose unit, map, join trait Monad[F[_]] extends Functor[F] { def unit[A](a: => A): F[A] def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] def map[A, B](fa: F[A])(f: A => B): F[B] = flatMap(fa)(a => unit(f(a))) def map2[A, B, C](fa: F[A], fb: F[B]) (f: (A, B) => C): F[C] = flatMap(fa)(a => map(fb)(b => f(a, b))) def product[A, B](ma: F[A], mb: F[B]): F[(A, B)] = map2(ma, mb)((a, b) => (a, b)) }
  41. 41. Monad laws: x.flatMap(f).flatMap(g) == x.flatMap(a => f(a).flatMap(g)) unit(x) flatMap f == f(x) flatMap(m)(unit) == m
  42. 42. References https://www.coursera.org/course/progfun https://www.coursera.org/course/reactive http://www.manning.com/bjarnason/ Structure and Interpretation of Computer Programs: Hal Abelson's, Jerry Sussman's and Julie Sussman's Programming in Scala, Second Edition: Martin Odersky, Lex Spoon, and Bill Venners http://www.cs.cmu.edu/~rwh/theses/okasaki.pdf

×