SlideShare ist ein Scribd-Unternehmen logo
1 von 44
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() {
def buyCoffee(cc: CreditCard, p: Payments): Coffee = {
val cup = Coffee(100)
p.charge(cc, cup.price)
cup
}
}
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")
}
}
}
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))
}
}
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
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
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
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
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)
}
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)
}
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))
}
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))
}
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
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]
object List {
def apply[A](as: A*): List[A] =
if (as.isEmpty) Nil
else Cons(as.head, apply(as.tail: _*))
}
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)
}
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)
}
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))
}
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
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
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))
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))
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)
}
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)
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]
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))
}
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))
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
}
String countryName(User user) {
try {
return Countries.findByCode(
PSTN.extractCountryCode(user.getPhone()));
}
catch (Exception npe) {
return null;
}
}
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
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
}
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))
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)
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)
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)
}
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)
}
}
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)
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
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))
}
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/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

Weitere ähnliche Inhalte

Was ist angesagt?

λ | Lenses
λ | Lensesλ | Lenses
λ | LensesOpen-IT
 
Monadologie
MonadologieMonadologie
Monadologieleague
 
Optics with monocle - Modeling the part and the whole
Optics with monocle - Modeling the part and the wholeOptics with monocle - Modeling the part and the whole
Optics with monocle - Modeling the part and the wholeIlan Godik
 
Introduction to Monads in Scala (2)
Introduction to Monads in Scala (2)Introduction to Monads in Scala (2)
Introduction to Monads in Scala (2)stasimus
 
Effector: we need to go deeper
Effector: we need to go deeperEffector: we need to go deeper
Effector: we need to go deeperVictor Didenko
 
Taking your side effects aside
Taking your side effects asideTaking your side effects aside
Taking your side effects aside💡 Tomasz Kogut
 
Kotlin Basics - Apalon Kotlin Sprint Part 2
Kotlin Basics - Apalon Kotlin Sprint Part 2Kotlin Basics - Apalon Kotlin Sprint Part 2
Kotlin Basics - Apalon Kotlin Sprint Part 2Kirill Rozov
 
How to extend map? Or why we need collections redesign? - Scalar 2017
How to extend map? Or why we need collections redesign? - Scalar 2017How to extend map? Or why we need collections redesign? - Scalar 2017
How to extend map? Or why we need collections redesign? - Scalar 2017Szymon Matejczyk
 
Hitchhiker's Guide to Functional Programming
Hitchhiker's Guide to Functional ProgrammingHitchhiker's Guide to Functional Programming
Hitchhiker's Guide to Functional ProgrammingSergey Shishkin
 
JDD2015: Functional programing and Event Sourcing - a pair made in heaven - e...
JDD2015: Functional programing and Event Sourcing - a pair made in heaven - e...JDD2015: Functional programing and Event Sourcing - a pair made in heaven - e...
JDD2015: Functional programing and Event Sourcing - a pair made in heaven - e...PROIDEA
 

Was ist angesagt? (16)

Beyond Scala Lens
Beyond Scala LensBeyond Scala Lens
Beyond Scala Lens
 
λ | Lenses
λ | Lensesλ | Lenses
λ | Lenses
 
Oh Composable World!
Oh Composable World!Oh Composable World!
Oh Composable World!
 
Monadologie
MonadologieMonadologie
Monadologie
 
Optics with monocle - Modeling the part and the whole
Optics with monocle - Modeling the part and the wholeOptics with monocle - Modeling the part and the whole
Optics with monocle - Modeling the part and the whole
 
Introduction to Monads in Scala (2)
Introduction to Monads in Scala (2)Introduction to Monads in Scala (2)
Introduction to Monads in Scala (2)
 
P3 2017 python_regexes
P3 2017 python_regexesP3 2017 python_regexes
P3 2017 python_regexes
 
Effector: we need to go deeper
Effector: we need to go deeperEffector: we need to go deeper
Effector: we need to go deeper
 
SDC - Einführung in Scala
SDC - Einführung in ScalaSDC - Einführung in Scala
SDC - Einführung in Scala
 
Taking your side effects aside
Taking your side effects asideTaking your side effects aside
Taking your side effects aside
 
Kotlin Basics - Apalon Kotlin Sprint Part 2
Kotlin Basics - Apalon Kotlin Sprint Part 2Kotlin Basics - Apalon Kotlin Sprint Part 2
Kotlin Basics - Apalon Kotlin Sprint Part 2
 
How to extend map? Or why we need collections redesign? - Scalar 2017
How to extend map? Or why we need collections redesign? - Scalar 2017How to extend map? Or why we need collections redesign? - Scalar 2017
How to extend map? Or why we need collections redesign? - Scalar 2017
 
Hitchhiker's Guide to Functional Programming
Hitchhiker's Guide to Functional ProgrammingHitchhiker's Guide to Functional Programming
Hitchhiker's Guide to Functional Programming
 
02 arrays
02 arrays02 arrays
02 arrays
 
JDD2015: Functional programing and Event Sourcing - a pair made in heaven - e...
JDD2015: Functional programing and Event Sourcing - a pair made in heaven - e...JDD2015: Functional programing and Event Sourcing - a pair made in heaven - e...
JDD2015: Functional programing and Event Sourcing - a pair made in heaven - e...
 
SVGo workshop
SVGo workshopSVGo workshop
SVGo workshop
 

Ähnlich wie Functional Programming in Scala: Immutable Data Structures and Pure Functions

Fp in scala with adts part 2
Fp in scala with adts part 2Fp in scala with adts part 2
Fp in scala with adts part 2Hang Zhao
 
Why Haskell Matters
Why Haskell MattersWhy Haskell Matters
Why Haskell Mattersromanandreg
 
Nat, List and Option Monoids - from scratch - Combining and Folding - an example
Nat, List and Option Monoids -from scratch -Combining and Folding -an exampleNat, List and Option Monoids -from scratch -Combining and Folding -an example
Nat, List and Option Monoids - from scratch - Combining and Folding - an examplePhilip Schwarz
 
Scala - where objects and functions meet
Scala - where objects and functions meetScala - where objects and functions meet
Scala - where objects and functions meetMario Fusco
 
Scala Functional Patterns
Scala Functional PatternsScala Functional Patterns
Scala Functional Patternsleague
 
[Expert Fridays] Александр Чичигин - Как перестать бояться и полюбить COQ
[Expert Fridays] Александр Чичигин - Как перестать бояться и полюбить COQ[Expert Fridays] Александр Чичигин - Как перестать бояться и полюбить COQ
[Expert Fridays] Александр Чичигин - Как перестать бояться и полюбить COQProvectus
 
Делаем пользовательское Api на базе Shapeless
Делаем пользовательское Api на базе ShapelessДелаем пользовательское Api на базе Shapeless
Делаем пользовательское Api на базе ShapelessВадим Челышов
 
Oh, All the things you'll traverse
Oh, All the things you'll traverseOh, All the things you'll traverse
Oh, All the things you'll traverseLuka Jacobowitz
 
Purely Functional Data Structures in Scala
Purely Functional Data Structures in ScalaPurely Functional Data Structures in Scala
Purely Functional Data Structures in ScalaVladimir Kostyukov
 
Fp in scala part 2
Fp in scala part 2Fp in scala part 2
Fp in scala part 2Hang Zhao
 
High Wizardry in the Land of Scala
High Wizardry in the Land of ScalaHigh Wizardry in the Land of Scala
High Wizardry in the Land of Scaladjspiewak
 
Ozma: Extending Scala with Oz concurrency
Ozma: Extending Scala with Oz concurrencyOzma: Extending Scala with Oz concurrency
Ozma: Extending Scala with Oz concurrencyBeScala
 
Scala or functional programming from a python developer's perspective
Scala or functional programming from a python developer's perspectiveScala or functional programming from a python developer's perspective
Scala or functional programming from a python developer's perspectivegabalese
 
関数潮流(Function Tendency)
関数潮流(Function Tendency)関数潮流(Function Tendency)
関数潮流(Function Tendency)riue
 
Functional programming with_scala
Functional programming with_scalaFunctional programming with_scala
Functional programming with_scalaRaymond Tay
 

Ähnlich wie Functional Programming in Scala: Immutable Data Structures and Pure Functions (20)

Fp in scala with adts part 2
Fp in scala with adts part 2Fp in scala with adts part 2
Fp in scala with adts part 2
 
Why Haskell Matters
Why Haskell MattersWhy Haskell Matters
Why Haskell Matters
 
Nat, List and Option Monoids - from scratch - Combining and Folding - an example
Nat, List and Option Monoids -from scratch -Combining and Folding -an exampleNat, List and Option Monoids -from scratch -Combining and Folding -an example
Nat, List and Option Monoids - from scratch - Combining and Folding - an example
 
Scala - where objects and functions meet
Scala - where objects and functions meetScala - where objects and functions meet
Scala - where objects and functions meet
 
Scala Functional Patterns
Scala Functional PatternsScala Functional Patterns
Scala Functional Patterns
 
[Expert Fridays] Александр Чичигин - Как перестать бояться и полюбить COQ
[Expert Fridays] Александр Чичигин - Как перестать бояться и полюбить COQ[Expert Fridays] Александр Чичигин - Как перестать бояться и полюбить COQ
[Expert Fridays] Александр Чичигин - Как перестать бояться и полюбить COQ
 
Scala best practices
Scala best practicesScala best practices
Scala best practices
 
Делаем пользовательское Api на базе Shapeless
Делаем пользовательское Api на базе ShapelessДелаем пользовательское Api на базе Shapeless
Делаем пользовательское Api на базе Shapeless
 
Oh, All the things you'll traverse
Oh, All the things you'll traverseOh, All the things you'll traverse
Oh, All the things you'll traverse
 
Purely Functional Data Structures in Scala
Purely Functional Data Structures in ScalaPurely Functional Data Structures in Scala
Purely Functional Data Structures in Scala
 
Thesis PPT
Thesis PPTThesis PPT
Thesis PPT
 
Thesis
ThesisThesis
Thesis
 
Scala by Luc Duponcheel
Scala by Luc DuponcheelScala by Luc Duponcheel
Scala by Luc Duponcheel
 
Fp in scala part 2
Fp in scala part 2Fp in scala part 2
Fp in scala part 2
 
High Wizardry in the Land of Scala
High Wizardry in the Land of ScalaHigh Wizardry in the Land of Scala
High Wizardry in the Land of Scala
 
Ozma: Extending Scala with Oz concurrency
Ozma: Extending Scala with Oz concurrencyOzma: Extending Scala with Oz concurrency
Ozma: Extending Scala with Oz concurrency
 
Scala or functional programming from a python developer's perspective
Scala or functional programming from a python developer's perspectiveScala or functional programming from a python developer's perspective
Scala or functional programming from a python developer's perspective
 
関数潮流(Function Tendency)
関数潮流(Function Tendency)関数潮流(Function Tendency)
関数潮流(Function Tendency)
 
Map, Reduce and Filter in Swift
Map, Reduce and Filter in SwiftMap, Reduce and Filter in Swift
Map, Reduce and Filter in Swift
 
Functional programming with_scala
Functional programming with_scalaFunctional programming with_scala
Functional programming with_scala
 

Functional Programming in Scala: Immutable Data Structures and Pure Functions

  • 2. class Cafe { def buyCoffee(cc: CreditCard): Coffee = { val cup = Coffee(100) cc.charge(100) cup } }
  • 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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
  • 16.
  • 17.
  • 18. 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: _*)) }
  • 19. 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) }
  • 20. 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) }
  • 21. 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)) }
  • 22. 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
  • 23. 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
  • 24. 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))
  • 25. 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))
  • 26. 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) }
  • 27. 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)
  • 28. 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]
  • 29. 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)) }
  • 30. 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))
  • 31. 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 }
  • 32. String countryName(User user) { try { return Countries.findByCode( PSTN.extractCountryCode(user.getPhone())); } catch (Exception npe) { return null; } }
  • 33. 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
  • 34. 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 }
  • 35. 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))
  • 36. 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)
  • 37. 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)
  • 38. 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) }
  • 39. 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) } }
  • 40. 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)
  • 41. 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
  • 42. 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)) }
  • 43. Monad laws: x.flatMap(f).flatMap(g) == x.flatMap(a => f(a).flatMap(g)) unit(x) flatMap f == f(x) flatMap(m)(unit) == m
  • 44. 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