SlideShare uma empresa Scribd logo
1 de 410
Baixar para ler offline
Functional Programming
& Event Sourcing
A pair made in heaven
twitter: @rabbitonweb, email: paul.szulc@gmail.com
Functional Programming
& Event Sourcing
A pair made in heaven
twitter: @rabbitonweb, email: paul.szulc@gmail.com2h brainwash
Functional Programming
● no assignment statements
● no assignment statements
● no variables
● no assignment statements
● no variables
● once given a value, never
change
● no assignment statements
● no variables
● once given a value, never
change
● no side-effects at all
● no assignment statements
● no variables
● once given a value, never
change
● no side-effects at all
“The functional programmer
sounds rather like a
mediæval monk,
“The functional programmer
sounds rather like a
mediæval monk, denying
himself the pleasures of life
“The functional programmer
sounds rather like a
mediæval monk, denying
himself the pleasures of life
in the hope that it will make
him virtuous.”
case class User(id: Long, fn: String, ln: String)
case class User(id: Long, fn: String, ln: String)
class Cache { def check(id: Long): Option[User] = ??? }
case class User(id: Long, fn: String, ln: String)
class Cache { def check(id: Long): Option[User] = ??? }
case class UserRepo(cache: Cache) {
def retrieve(id: Long): User = ???
}
case class User(id: Long, fn: String, ln: String)
class Cache { def check(id: Long): Option[User] = ??? }
case class UserRepo(cache: Cache) {
def retrieve(id: Long): User = ???
}
class UserFinder(cache: Cache, repo: UserRepo) {
}
case class User(id: Long, fn: String, ln: String)
class Cache { def check(id: Long): Option[User] = ??? }
case class UserRepo(cache: Cache) {
def retrieve(id: Long): User = ???
}
class UserFinder(cache: Cache, repo: UserRepo) {
def findUser(id: Long): User = {
}
}
case class User(id: Long, fn: String, ln: String)
class Cache { def check(id: Long): Option[User] = ??? }
case class UserRepo(cache: Cache) {
def retrieve(id: Long): User = ???
}
class UserFinder(cache: Cache, repo: UserRepo) {
def findUser(id: Long): User = {
val maybeUser: Option[User] = cache.check(id)
}
}
case class User(id: Long, fn: String, ln: String)
class Cache { def check(id: Long): Option[User] = ??? }
case class UserRepo(cache: Cache) {
def retrieve(id: Long): User = ???
}
class UserFinder(cache: Cache, repo: UserRepo) {
def findUser(id: Long): User = {
val maybeUser: Option[User] = cache.check(id)
if (maybeUser.isDefined) {
maybeUser.get
}
}
}
case class User(id: Long, fn: String, ln: String)
class Cache { def check(id: Long): Option[User] = ??? }
case class UserRepo(cache: Cache) {
def retrieve(id: Long): User = ???
}
class UserFinder(cache: Cache, repo: UserRepo) {
def findUser(id: Long): User = {
val maybeUser: Option[User] = cache.check(id)
if (maybeUser.isDefined) {
maybeUser.get
} else {
val user: User = repo.retrieve(id)
cache.insert(user.id, user)
}
}
}
No concept of ‘time’
f(input): output
input f(input): output
input f(input): output output
input f(input): output output g(input): output
input f(input): output output g(input): output output
input f(input): output output g(input): output output
h = g o f
input f(input): output output g(input): output output
Modularity & composition
h = g o f
input f(input): output output g(input): output output
/**
* This function returns a reversed list
* @param list A list to be reversed
* @return A reversed list
*/
public List<T> reverse(List<t> list) { ??? }
/**
* This function returns a reversed list
* @param list A list to be reversed
* @return A reversed list
*/
public List<T> reverse(List<t> list) { ??? }
/**
* This function returns a reversed list
* @param list A list to be reversed
public List<T> reverse(List<t> list) { ??? }
/**
* This function returns a reversed list
public List<T> reverse(List<t> list) { ??? }
/**
public List<T> reverse(List<t> list) { ??? }
public List<T> reverse(List<t> list) { ??? }
public List<T> reverse(List<t> list) {
return list.sort();
}
def smdfknmsdfp[A](a: A): A = ???
def smdfknmsdfp[A](a: A): A = a
def identity[A](a: A): A = a
def smdfknmsdfp[A](a: A): A = a
def smdfknmsdfp(a: Int): Int = ???
def smdfknmsdfp(a: Int): Int = a
= a + 10
= 10
def sum(list: List[Int]): Int =
def sum(list: List[Int]): Int = list match {
case Nil => 0
def sum(list: List[Int]): Int = list match {
case Nil => 0
case num :: tail => num + sum(tail)
}
def sum(list: List[Int]): Int = list match {
case Nil => 0
case num :: tail => num + sum(tail)
}
def reduce[T, K]
(list: List[T]): K =
def reduce[T, K]
(list: List[T]): K = list match {
case Nil => zero
def reduce[T, K]
(zero: K)
(list: List[T]): K = list match {
case Nil => zero
def reduce[T, K]
(zero: K)
(list: List[T]): K = list match {
case Nil => zero
case head :: tail => op(head)(reduce(op)(zero)(tail))
}
def reduce[T, K](op: T => K => K)
(zero: K)
(list: List[T]): K = list match {
case Nil => zero
case head :: tail => op(head)(reduce(op)(zero)(tail))
}
def reduce[T, K](op: T => K => K)
(zero: K)
(list: List[T]): K = list match {
case Nil => zero
case head :: tail => op(head)(reduce(op)(zero)(tail))
}
def reduce[T, K](op: T => K => K)
(zero: K)
(list: List[T]): K = list match {
case Nil => zero
case head :: tail => op(head)(reduce(op)(zero)(tail))
}
def sum: List[Int] => Int =
def reduce[T, K](op: T => K => K)
(zero: K)
(list: List[T]): K = list match {
case Nil => zero
case head :: tail => op(head)(reduce(op)(zero)(tail))
}
def sum: List[Int] => Int = reduce(plus)(0)
def reduce[T, K](op: T => K => K)
(zero: K)
(list: List[T]): K = list match {
case Nil => zero
case head :: tail => op(head)(reduce(op)(zero)(tail))
}
def reduce[T, K](op: T => K => K)
(zero: K)
(list: List[T]): K = list match {
case Nil => zero
case head :: tail => op(head)(reduce(op)(zero)(tail))
}
def product: List[Int] => Int =
def reduce[T, K](op: T => K => K)
(zero: K)
(list: List[T]): K = list match {
case Nil => zero
case head :: tail => op(head)(reduce(op)(zero)(tail))
}
def product: List[Int] => Int = reduce(multiply)(1)
def reduce[T, K](op: T => K => K)
(zero: K)
(list: List[T]): K = list match {
case Nil => zero
case head :: tail => op(head)(reduce(op)(zero)(tail))
}
def reduce[T, K](op: T => K => K)
(zero: K)
(list: List[T]): K = list match {
case Nil => zero
case head :: tail => op(head)(reduce(op)(zero)(tail))
}
def anyTrue: List[Boolean] => Boolean =
def reduce[T, K](op: T => K => K)
(zero: K)
(list: List[T]): K = list match {
case Nil => zero
case head :: tail => op(head)(reduce(op)(zero)(tail))
}
def anyTrue: List[Boolean] => Boolean = reduce(or)(false)
def reduce[T, K](op: T => K => K)
(zero: K)
(list: List[T]): K = list match {
case Nil => zero
case head :: tail => op(head)(reduce(op)(zero)(tail))
}
def reduce[T, K](op: T => K => K)
(zero: K)
(list: List[T]): K = list match {
case Nil => zero
case head :: tail => op(head)(reduce(op)(zero)(tail))
}
def allTrue: List[Boolean] => Boolean =
def reduce[T, K](op: T => K => K)
(zero: K)
(list: List[T]): K = list match {
case Nil => zero
case head :: tail => op(head)(reduce(op)(zero)(tail))
}
def allTrue: List[Boolean] => Boolean = reduce(and)(true)
def reduce[T, K](op: T => K => K)
(zero: K)
(list: List[T]): K = list match {
case Nil => zero
case head :: tail => op(head)(reduce(op)(zero)(tail))
}
def reduce[T, K](op: T => K => K)
(zero: K)
(list: List[T]): K = list match {
case Nil => zero
case head :: tail => op(head)(reduce(op)(zero)(tail))
}
A :: B :: C :: D :: E :: Nil
def reduce[T, K](op: T => K => K)
(zero: K)
(list: List[T]): K = list match {
case Nil => zero
case head :: tail => op(head)(reduce(op)(zero)(tail))
}
A :: B :: C :: D :: E :: Nil
A :: B :: C :: D :: E :: Nil
def reduce[T, K](op: T => K => K)
(zero: K)
(list: List[T]): K = list match {
case Nil => zero
case head :: tail => op(head)(reduce(op)(zero)(tail))
}
A :: B :: C :: D :: E :: Nil
A :: B :: C :: D :: E :: zero
def reduce[T, K](op: T => K => K)
(zero: K)
(list: List[T]): K = list match {
case Nil => zero
case head :: tail => op(head)(reduce(op)(zero)(tail))
}
A :: B :: C :: D :: E :: Nil
A op B op C op D op E op zero
def <++>[T](a: List[T])(b: List[T]): List[T] =
def <++>[T](a: List[T])(b: List[T]): List[T] =
reduce(::[T])(b)(a)
def lgth[T]: List[T] => Int = reduce(<+>)(0)
def <+>[T](el: T)(len: Int) = 1 + len
def lgth[T]: List[T] => Int = reduce(<+>)(0)
def double(x: Int) = 2 * x
def double(x: Int) = 2 * x
def doubleAll: List[Int] => List[Int] =
def double(x: Int) = 2 * x
def doubleAll: List[Int] => List[Int] =
reduce(<+>)(List.empty[Int])
def double(x: Int) = 2 * x
def <+>(el: Int)(list: List[Int]) = double(el) :: list
def doubleAll: List[Int] => List[Int] =
reduce(<+>)(List.empty[Int])
def double(x: Int) = 2 * x
def <+>(el: Int)(list: List[Int]) = double(el) :: list
def doubleAll: List[Int] => List[Int] =
reduce(<+>)(List.empty[Int])
def map[T](f: T => T) =
def map[T](f: T => T) = {
reduce(<+>)(List.empty[T])(_)
}
def map[T](f: T => T) = {
def <+>(el: T)(list: List[T]) = f(el) :: list
reduce(<+>)(List.empty[T])(_)
}
def map[T](f: T => T) = {
def <+>(el: T)(list: List[T]) = f(el) :: list
reduce(<+>)(List.empty[T])(_)
}
def doubleAll: List[Int] => List[Int] = map(double)
def map[T](f: T => T) = {
def <+>(el: T)(list: List[T]) = f(el) :: list
reduce(<+>)(List.empty[T])(_)
}
def double(x: Int) = 2 * x
def doubleAll: List[Int] => List[Int] = map(double)
case class TreeOf[T](label: T, subtrees: List[TreeOf[T]])
case class TreeOf[T](label: T, subtrees: List[TreeOf[T]])
def redtree[T, K,M]
= tree match {
case TreeOf(label, subtrees) =>
}
case class TreeOf[T](label: T, subtrees: List[TreeOf[T]])
def redtree[T, K,M](op1: T => K => M)
= tree match {
case TreeOf(label, subtrees) =>
op1(label)()
}
case class TreeOf[T](label: T, subtrees: List[TreeOf[T]])
def redtree[T, K,M](op1: T => K => M)
= tree match {
case TreeOf(label, subtrees) =>
op1(label)(_redtree[T, K,M](op1)(op2)(zero)(subtrees))
}
def redtree[T, K,M](op1: T => K => M)(op2: M => K => K)(zero: K)(tree:
TreeOf[T]): M = tree match {
case TreeOf(label, subtrees) =>
op1(label)(_redtree[T, K,M](op1)(op2)(zero)(subtrees))
}
def _redtree[T, K, M](op1: T => K => M)(op2: M => K => K)(zero: K)
(subtrees: List[TreeOf[T]]): K =
def redtree[T, K,M](op1: T => K => M)(op2: M => K => K)(zero: K)(tree:
TreeOf[T]): M = tree match {
case TreeOf(label, subtrees) =>
op1(label)(_redtree[T, K,M](op1)(op2)(zero)(subtrees))
}
def _redtree[T, K, M](op1: T => K => M)(op2: M => K => K)(zero: K)
(subtrees: List[TreeOf[T]]): K = subtrees match {
case Nil => zero
def redtree[T, K,M](op1: T => K => M)(op2: M => K => K)(zero: K)(tree:
TreeOf[T]): M = tree match {
case TreeOf(label, subtrees) =>
op1(label)(_redtree[T, K,M](op1)(op2)(zero)(subtrees))
}
def _redtree[T, K, M](op1: T => K => M)(op2: M => K => K)(zero: K)
(subtrees: List[TreeOf[T]]): K = subtrees match {
case Nil => zero
case subtree :: rest =>
}
def redtree[T, K,M](op1: T => K => M)(op2: M => K => K)(zero: K)(tree:
TreeOf[T]): M = tree match {
case TreeOf(label, subtrees) =>
op1(label)(_redtree[T, K,M](op1)(op2)(zero)(subtrees))
}
def _redtree[T, K, M](op1: T => K => M)(op2: M => K => K)(zero: K)
(subtrees: List[TreeOf[T]]): K = subtrees match {
case Nil => zero
case subtree :: rest =>
redtree(op1)(op2)(zero)(subtree))
}
def redtree[T, K,M](op1: T => K => M)(op2: M => K => K)(zero: K)(tree:
TreeOf[T]): M = tree match {
case TreeOf(label, subtrees) =>
op1(label)(_redtree[T, K,M](op1)(op2)(zero)(subtrees))
}
def _redtree[T, K, M](op1: T => K => M)(op2: M => K => K)(zero: K)
(subtrees: List[TreeOf[T]]): K = subtrees match {
case Nil => zero
case subtree :: rest =>
redtree(op1)(op2)(zero)(subtree))
(_redtree(op1)(op2)(zero)(rest))
}
def redtree[T, K,M](op1: T => K => M)(op2: M => K => K)(zero: K)(tree:
TreeOf[T]): M = tree match {
case TreeOf(label, subtrees) =>
op1(label)(_redtree[T, K,M](op1)(op2)(zero)(subtrees))
}
def _redtree[T, K, M](op1: T => K => M)(op2: M => K => K)(zero: K)
(subtrees: List[TreeOf[T]]): K = subtrees match {
case Nil => zero
case subtree :: rest =>
op2(redtree(op1)(op2)(zero)(subtree))
(_redtree(op1)(op2)(zero)(rest))
}
def redtree[T, K,M](op1: T => K => M)(op2: M => K => K)(zero: K)(tree:
TreeOf[T]): M = tree match {
case TreeOf(label, subtrees) =>
op1(label)(_redtree[T, K,M](op1)(op2)(zero)(subtrees))
}
def sumtree = redtree(plus)(plus)(0)(_)
def labels[T]: TreeOf[T] => List[T] =
redtree(::[T])(<++>[T])(List.empty[T])
def mapTree[T, K](f: T => K): TreeOf[T] => TreeOf[K] = {
def <+>(label: T)(subtrees: List[TreeOf[K]]) =
TreeOf(f(label), subtrees)
redtree(<+>)(::[TreeOf[K]])(List.empty[TreeOf[K]])
}
“Why Functional Programming Matters”
J. Hughes
http://comjnl.oxfordjournals.org/content/32/2/98.
full.pdf
“Why Functional Programming Matters”
J. Hughes, Nov. 1988
http://comjnl.oxfordjournals.org/content/32/2/98.
full.pdf
Soft introduction
“Why Functional Programming Matters”
J. Hughes, Nov. 1988
http://comjnl.oxfordjournals.org/content/32/2/98.
full.pdf
“Program Design by Calculation”
J.N. Oliveira
http://www4.di.uminho.pt/~jno/ps/pdbc_part.pdf
“Program Design by Calculation”
J.N. Oliveira, Draft
http://www4.di.uminho.pt/~jno/ps/pdbc_part.pdf
Patterns in FP World
“Program Design by Calculation”
J.N. Oliveira, Draft
http://www4.di.uminho.pt/~jno/ps/pdbc_part.pdf
Patterns in FP World Math Matters
“Program Design by Calculation”
J.N. Oliveira, Draft
http://www4.di.uminho.pt/~jno/ps/pdbc_part.pdf
“A lengthy approach to Haskell fundamentals”
http://www.davesquared.net/2012/05/lengthy-
approach-to-haskell.html
Functional Programming
Functional Programming
https://xkcd.com/1312/
How to do something
useful?
How to do something
useful?
Monads
Asking the right question
WTF Monad?
WTF Monad?
“Hi”
“Sup?”
“I'm ready to write my first program. How
can I print some stuff to console?”
“Use IO Monad”
“What the *** is a monad?”
WTF Monad?
1. We understand code
1. We understand code
2. It is easier to talk about related ideas and
then abstract to something more general
1. We understand code
2. It is easier to talk about related ideas and
then abstract to something more general
3. Having abstract model created, we tend to
create metaphors
1. We understand code
2. It is easier to talk about related ideas and
then abstract to something more general
3. Having abstract model created, we tend to
create metaphors
4. Having all above we can formalize
Code & related ideas
Android, iOS, Windows Phone
Partner Lookup
case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace)
case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace)
case class WorkPlace(name: String, street: String)
case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace)
case class WorkPlace(name: String, street: String)
def partnerLookup(geek: Geek): String =
case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace)
case class WorkPlace(name: String, street: String)
def partnerLookup(geek: Geek): String = geek.partner.workPlace.street
case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace)
case class WorkPlace(name: String, street: String)
def partnerLookup(geek: Geek): String = geek.partner.workPlace.street
val cheeseCakeFactory = WorkPlace("Cheese Cake Factory", "Cake Street 1")
val university = WorkPlace("University", "Academic St. 10")
case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace)
case class WorkPlace(name: String, street: String)
def partnerLookup(geek: Geek): String = geek.partner.workPlace.street
val cheeseCakeFactory = WorkPlace("Cheese Cake Factory", "Cake Street 1")
val university = WorkPlace("University", "Academic St. 10")
var penny = Geek("Penny", workPlace = cheeseCakeFactory)
var leonard = Geek("Leonard", workPlace = university)
case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace)
case class WorkPlace(name: String, street: String)
def partnerLookup(geek: Geek): String = geek.partner.workPlace.street
val cheeseCakeFactory = WorkPlace("Cheese Cake Factory", "Cake Street 1")
val university = WorkPlace("University", "Academic St. 10")
var penny = Geek("Penny", workPlace = cheeseCakeFactory)
var leonard = Geek("Leonard", workPlace = university)
penny = penny.copy(partner = leonard)
leonard = leonard.copy(partner = penny)
case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace)
case class WorkPlace(name: String, street: String)
def partnerLookup(geek: Geek): String = geek.partner.workPlace.street
val cheeseCakeFactory = WorkPlace("Cheese Cake Factory", "Cake Street 1")
val university = WorkPlace("University", "Academic St. 10")
var penny = Geek("Penny", workPlace = cheeseCakeFactory)
var leonard = Geek("Leonard", workPlace = university)
penny = penny.copy(partner = leonard)
leonard = leonard.copy(partner = penny)
println(partnerLookup(leonard))
println(partnerLookup(penny))
case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace)
case class WorkPlace(name: String, street: String)
def partnerLookup(geek: Geek): String = geek.partner.workPlace.street
val cheeseCakeFactory = WorkPlace("Cheese Cake Factory", "Cake Street 1")
val university = WorkPlace("University", "Academic St. 10")
var penny = Geek("Penny", workPlace = cheeseCakeFactory)
var leonard = Geek("Leonard", workPlace = university)
penny = penny.copy(partner = leonard)
leonard = leonard.copy(partner = penny)
println(partnerLookup(leonard)) // output: "Cake Street 1"
println(partnerLookup(penny))
case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace)
case class WorkPlace(name: String, street: String)
def partnerLookup(geek: Geek): String = geek.partner.workPlace.street
val cheeseCakeFactory = WorkPlace("Cheese Cake Factory", "Cake Street 1")
val university = WorkPlace("University", "Academic St. 10")
var penny = Geek("Penny", workPlace = cheeseCakeFactory)
var leonard = Geek("Leonard", workPlace = university)
penny = penny.copy(partner = leonard)
leonard = leonard.copy(partner = penny)
println(partnerLookup(leonard)) // output: "Cake Street 1"
println(partnerLookup(penny)) // output: "Academic St. 10"
“This app sucks!”
case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace)
case class WorkPlace(name: String, street: String)
def partnerLookup(geek: Geek): String = geek.partner.workPlace.street
case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace)
case class WorkPlace(name: String, street: String)
def partnerLookup(geek: Geek): String = geek.partner.workPlace.street
case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace)
case class WorkPlace(name: String, street: String)
def partnerLookup(geek: Geek): String = geek.partner.workPlace.street
val university = WorkPlace("University", "Academic St. 10")
case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace)
case class WorkPlace(name: String, street: String)
def partnerLookup(geek: Geek): String = geek.partner.workPlace.street
val university = WorkPlace("University", "Academic St. 10")
val rajesh = Geek("Rajesh", workPlace = university)
case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace)
case class WorkPlace(name: String, street: String)
def partnerLookup(geek: Geek): String = geek.partner.workPlace.street
val university = WorkPlace("University", "Academic St. 10")
val rajesh = Geek("Rajesh", workPlace = university)
println(partnerLookup(rajesh))
case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace)
case class WorkPlace(name: String, street: String)
def partnerLookup(geek: Geek): String = geek.partner.workPlace.street
val university = WorkPlace("University", "Academic St. 10")
val rajesh = Geek("Rajesh", workPlace = university)
println(partnerLookup(rajesh))
// java.lang.NullPointerException
// at wtf.examples.A1$.partnerLookUp(A1.scala:14)
// at ...
case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace)
case class WorkPlace(name: String, street: String)
def partnerLookup(geek: Geek): String = geek.partner.workPlace.street
val university = WorkPlace("University", "Academic St. 10")
val rajesh = Geek("Rajesh", workPlace = university)
println(partnerLookup(rajesh))
// java.lang.NullPointerException
// at wtf.examples.A1$.partnerLookUp(A1.scala:14)
// at ...
def partnerLookup(geek: Geek): String = {
}
def partnerLookup(geek: Geek): String = {
if(geek != null) {
}
def partnerLookup(geek: Geek): String = {
if(geek != null) {
if(geek.partner != null) {
}
def partnerLookup(geek: Geek): String = {
if(geek != null) {
if(geek.partner != null) {
if(geek.partner.workPlace != null) {
}
def partnerLookup(geek: Geek): String = {
if(geek != null) {
if(geek.partner != null) {
if(geek.partner.workPlace != null) {
if(geek.partner.workPlace.street != null) {
}
def partnerLookup(geek: Geek): String = {
if(geek != null) {
if(geek.partner != null) {
if(geek.partner.workPlace != null) {
if(geek.partner.workPlace.street != null) {
return geek.partner.workPlace.street
}
}
}
}
}
def partnerLookup(geek: Geek): String = {
if(geek != null) {
if(geek.partner != null) {
if(geek.partner.workPlace != null) {
if(geek.partner.workPlace.street != null) {
return geek.partner.workPlace.street
}
}
}
}
"not found"
}
val cheeseCakeFactory = WorkPlace("Cheese Cake Factory", "Cake Street 1")
val university = WorkPlace("University", "Academic St. 10")
var penny = Geek("Penny", workPlace = cheeseCakeFactory)
var leonard = Geek("Leonard", workPlace = university)
val rajesh = Geek("Rajesh", workPlace = university)
penny = penny.copy(partner = leonard)
leonard = leonard.copy(partner = penny)
println(partnerLookup(leonard)) // output: "Cake Street 1"
println(partnerLookup(penny)) // output: "Academic St. 10"
println(partnerLookup(rajesh)) // output: "not found"
def partnerLookup(geek: Geek): String = {
if(geek != null) {
if(geek.partner != null) {
if(geek.partner.workPlace != null) {
if(geek.partner.workPlace.street != null) {
return geek.partner.workPlace.street
}
}
}
}
"not found"
}
Call me: Maybe
sealed trait Maybe[+A]
sealed trait Maybe[+A]
case class Some[+A](value: A) extends Maybe[A]
sealed trait Maybe[+A]
case class Some[+A](value: A) extends Maybe[A]
case object None extends Maybe[Nothing]
sealed trait Maybe[+A]
case class Some[+A](value: A) extends Maybe[A]
case object None extends Maybe[Nothing]
object Maybe {
def apply[A](value: A) = Some(value)
}
sealed trait Maybe[+A] {
}
object Maybe {
def apply[A](value: A) = Some(value)
}
case class Some[+A](value: A) extends Maybe[A] {
}
case object None extends Maybe[Nothing] {
}
sealed trait Maybe[+A] {
def andThen[B](f: A => Maybe[B]): Maybe[B]
}
object Maybe {
def apply[A](value: A) = Some(value)
}
case class Some[+A](value: A) extends Maybe[A] {
}
case object None extends Maybe[Nothing] {
}
sealed trait Maybe[+A] {
def andThen[B](f: A => Maybe[B]): Maybe[B]
}
object Maybe {
def apply[A](value: A) = Some(value)
}
case class Some[+A](value: A) extends Maybe[A] {
def andThen[B](f: A => Maybe[B]): Maybe[B] = f(value)
}
case object None extends Maybe[Nothing] {
}
sealed trait Maybe[+A] {
def andThen[B](f: A => Maybe[B]): Maybe[B]
}
object Maybe {
def apply[A](value: A) = Some(value)
}
case class Some[+A](value: A) extends Maybe[A] {
def andThen[B](f: A => Maybe[B]): Maybe[B] = f(value)
}
case object None extends Maybe[Nothing] {
def andThen[B](f: Nothing => Maybe[B]): Maybe[B] = this
}
case class Geek(name: String, partner: Maybe[Geek] = wtf.monads.None, workPlace: Maybe[WorkPlace])
case class WorkPlace(name: String, street: Maybe[String])
def partnerLookup(geek: Geek): String =
case class Geek(name: String, partner: Maybe[Geek] = wtf.monads.None, workPlace: Maybe[WorkPlace])
case class WorkPlace(name: String, street: Maybe[String])
def partnerLookup(geek: Geek): String =
geek.partner
case class Geek(name: String, partner: Maybe[Geek] = wtf.monads.None, workPlace: Maybe[WorkPlace])
case class WorkPlace(name: String, street: Maybe[String])
def partnerLookup(geek: Geek): String =
geek.partner.andThen(p => p.workPlace)
case class Geek(name: String, partner: Maybe[Geek] = wtf.monads.None, workPlace: Maybe[WorkPlace])
case class WorkPlace(name: String, street: Maybe[String])
def partnerLookup(geek: Geek): String =
geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street)
case class Geek(name: String, partner: Maybe[Geek] = wtf.monads.None, workPlace: Maybe[WorkPlace])
case class WorkPlace(name: String, street: Maybe[String])
def partnerLookup(geek: Geek): String =
geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match {
case wtf.monads.Some(street) => street
}
case class Geek(name: String, partner: Maybe[Geek] = wtf.monads.None, workPlace: Maybe[WorkPlace])
case class WorkPlace(name: String, street: Maybe[String])
def partnerLookup(geek: Geek): String =
geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match {
case wtf.monads.Some(street) => street
case wtf.monads.None => "not found"
}
val cheeseCakeFactory = WorkPlace("Cheese Cake Factory", "Cake Street 1")
val university = WorkPlace("University", "Academic St. 10")
var penny = Geek("Penny", workPlace = cheeseCakeFactory)
var leonard = Geek("Leonard", workPlace = university)
val rajesh = Geek("Rajesh", workPlace = university)
penny = penny.copy(partner = leonard)
leonard = leonard.copy(partner = penny)
println(partnerLookup(leonard))
println(partnerLookup(penny))
println(partnerLookup(rajesh))
val cheeseCakeFactory = WorkPlace("Cheese Cake Factory", "Cake Street 1")
val university = WorkPlace("University", "Academic St. 10")
var penny = Geek("Penny", workPlace = cheeseCakeFactory)
var leonard = Geek("Leonard", workPlace = university)
val rajesh = Geek("Rajesh", workPlace = university)
penny = penny.copy(partner = leonard)
leonard = leonard.copy(partner = penny)
println(partnerLookup(leonard)) // output: "Cake Street 1"
println(partnerLookup(penny)) // output: "Academic St. 10"
println(partnerLookup(rajesh)) // output: "not found"
def partnerLookup(geek: Geek): String =
geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match {
case wtf.monads.Some(street) => street
case wtf.monads.None => "not found"
}
def partnerLookup(geek: Geek): String = geek.partner.workPlace.street
def partnerLookup(geek: Geek): String =
geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match {
case wtf.monads.Some(street) => street
case wtf.monads.None => "not found"
}
def partnerLookup(geek: Geek): String = geek.partner.workPlace.street
def partnerLookup(geek: Geek): String =
geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match {
case wtf.monads.Some(street) => street
case wtf.monads.None => "not found"
}
def partnerLookup(geek: Geek): String =
geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match {
case wtf.monads.Some(street) => street
case wtf.monads.None => "not found"
}
def partnerLookup(geek: Geek): String =
geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match {
case wtf.monads.Some(street) => street
case wtf.monads.None => "not found"
}
def partnerLookup(geek: Geek): String =
def partnerLookup(geek: Geek): Maybe[String] =
geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match {
case wtf.monads.Some(street) => street
case wtf.monads.None => "not found"
}
def partnerLookup(geek: Geek): Maybe[String] =
with a little bit of magic
For-comprehension
def partnerLookup(geek: Geek): Maybe[String] =
geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match {
case wtf.monads.Some(street) => street
case wtf.monads.None => "not found"
}
def partnerLookup(geek: Geek): Maybe[String] = {
}
def partnerLookup(geek: Geek): Maybe[String] =
geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match {
case wtf.monads.Some(street) => street
case wtf.monads.None => "not found"
}
def partnerLookup(geek: Geek): Maybe[String] = {
import Magic._
}
def partnerLookup(geek: Geek): Maybe[String] =
geek.partner.andThen(g => g.workPlace).andThen(wp => wp.street) match {
case wtf.monads.Some(street) => street
case wtf.monads.None => "not found"
}
def partnerLookup(geek: Geek): Maybe[String] = {
import Magic._
for {
p <- geek.partner
wp <- p.workPlace
s <- wp.street
} yield (s)
}
def partnerLookup(geek: Geek): Maybe[String] =
geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match {
case wtf.monads.Some(street) => street
case wtf.monads.None => "not found"
}
def partnerLookup(geek: Geek): Maybe[String] = {
import Magic._
for {
p <- geek.partner
wp <- p.workPlace
s <- wp.street
} yield (s)
}
def partnerLookup(geek: Geek): Maybe[String] =
geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match {
case wtf.monads.Some(street) => street
case wtf.monads.None => "not found"
}
def partnerLookup(geek: Geek): Maybe[String] = {
import Magic._
for {
p <- geek.partner
wp <- p.workPlace
s <- wp.street
} yield (s)
}
def partnerLookup(geek: Geek): Maybe[String] =
geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match {
case wtf.monads.Some(street) => street
case wtf.monads.None => "not found"
}
def partnerLookup(geek: Geek): Maybe[String] = {
import Magic._
for {
p <- geek.partner
wp <- p.workPlace
s <- wp.street
} yield (s)
}
def partnerLookup(geek: Geek): Maybe[String] =
geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match {
case wtf.monads.Some(street) => street
case wtf.monads.None => "not found"
}
def partnerLookup(geek: Geek): Maybe[String] = {
import Magic._
for {
p <- geek.partner
wp <- p.workPlace
s <- wp.street
} yield (s)
}
def partnerLookup(geek: Geek): Maybe[String] =
geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match {
case wtf.monads.Some(street) => street
case wtf.monads.None => "not found"
}
def partnerLookup(geek: Geek): Maybe[String] = {
import Magic._
for {
p <- geek.partner
wp <- p.workPlace
s <- wp.street
} yield (s)
}
def partnerLookup(geek: Geek): Maybe[String] =
geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match {
case wtf.monads.Some(street) => street
case wtf.monads.None => "not found"
}
def partnerLookup(geek: Geek): Maybe[String] = {
import Magic._
for {
p <- geek.partner
wp <- p.workPlace
s <- wp.street
} yield (s)
}
var leonard = Geek("Leonard", workPlace = university, partner = penny)
partnerLookup(leonard) // Some("Cake Street 1")
// how to print it?
sealed trait Maybe[+A] {
def andThen[B](f: A => Maybe[B]): Maybe[B]
}
object Maybe {
def apply[A](value: A) = Some(value)
}
case class Some[+A](value: A) extends Maybe[A] {
def andThen[B](f: A => Maybe[B]): Maybe[B] = f(value)
}
case object None extends Maybe[Nothing] {
def andThen[B](f: Nothing => Maybe[B]): Maybe[B] = this
}
sealed trait Maybe[+A] {
def andThen[B](f: A => Maybe[B]): Maybe[B]
def within[B](f: A => B): Maybe[B]
}
object Maybe {
def apply[A](value: A) = Some(value)
}
case class Some[+A](value: A) extends Maybe[A] {
def andThen[B](f: A => Maybe[B]): Maybe[B] = f(value)
}
case object None extends Maybe[Nothing] {
def andThen[B](f: Nothing => Maybe[B]): Maybe[B] = this
}
sealed trait Maybe[+A] {
def andThen[B](f: A => Maybe[B]): Maybe[B]
def within[B](f: A => B): Maybe[B]
}
object Maybe {
def apply[A](value: A) = Some(value)
}
case class Some[+A](value: A) extends Maybe[A] {
def andThen[B](f: A => Maybe[B]): Maybe[B] = f(value)
}
case object None extends Maybe[Nothing] {
def andThen[B](f: Nothing => Maybe[B]): Maybe[B] = this
}
Reuse existing functionality like println
sealed trait Maybe[+A] {
def andThen[B](f: A => Maybe[B]): Maybe[B]
def within[B](f: A => B): Maybe[B]
}
object Maybe {
def apply[A](value: A) = Some(value)
}
case class Some[+A](value: A) extends Maybe[A] {
def andThen[B](f: A => Maybe[B]): Maybe[B] = f(value)
def within[B](f: A => B): Maybe[B] = Maybe(f(value))
}
case object None extends Maybe[Nothing] {
def andThen[B](f: Nothing => Maybe[B]): Maybe[B] = this
}
sealed trait Maybe[+A] {
def andThen[B](f: A => Maybe[B]): Maybe[B]
def within[B](f: A => B): Maybe[B]
}
object Maybe {
def apply[A](value: A) = Some(value)
}
case class Some[+A](value: A) extends Maybe[A] {
def andThen[B](f: A => Maybe[B]): Maybe[B] = f(value)
def within[B](f: A => B): Maybe[B] = Maybe(f(value))
}
case object None extends Maybe[Nothing] {
def andThen[B](f: Nothing => Maybe[B]): Maybe[B] = this
def within[B](f: Nothing => B): Maybe[B] = this
}
var leonard = Geek("Leonard", workPlace = university, partner = penny)
partnerLookup(leonard) // Some("Cake Street 1")
// how to print it?
var leonard = Geek("Leonard", workPlace = university, partner = penny)
partnerLookup(leonard)
var leonard = Geek("Leonard", workPlace = university, partner = penny)
partnerLookup(leonard).within(println) // output: "Cake Street 1"
Android, iOS, Windows Phone
Ship Inventory
case class Pirate(name: String, ships: Many[Ship])
case class Ship(name: String, hold: Hold)
case class Hold(barrel: Many[Barrel])
case class Barrel(amount: Int)
case class Pirate(name: String, ships: Many[Ship])
case class Ship(name: String, hold: Hold)
case class Hold(barrel: Many[Barrel])
case class Barrel(amount: Int)
val jack = Pirate("Captain Jack Sparrow", Many(blackPearl, whitePearl))
case class Pirate(name: String, ships: Many[Ship])
case class Ship(name: String, hold: Hold)
case class Hold(barrel: Many[Barrel])
case class Barrel(amount: Int)
val blackPearl = Ship("Black Pearl", blackHold)
val whitePearl = Ship("White Pearl", whiteHold)
val jack = Pirate("Captain Jack Sparrow", Many(blackPearl, whitePearl))
case class Pirate(name: String, ships: Many[Ship])
case class Ship(name: String, hold: Hold)
case class Hold(barrel: Many[Barrel])
case class Barrel(amount: Int)
val blackHold = Hold(Empty)
val whiteHold = Hold(Many(Barrel(20), Barrel(10)))
val blackPearl = Ship("Black Pearl", blackHold)
val whitePearl = Ship("White Pearl", whiteHold)
val jack = Pirate("Captain Jack Sparrow", Many(blackPearl, whitePearl))
sealed trait Many[+A]
sealed trait Many[+A]
case class Const[+A](head: A, tail: Many[A]) extends Many[A]
sealed trait Many[+A]
case class Const[+A](head: A, tail: Many[A]) extends Many[A]
case object Empty extends Many[Nothing]
sealed trait Many[+A]
case class Const[+A](head: A, tail: Many[A]) extends Many[A]
case object Empty extends Many[Nothing]
object Many {
def apply[A](elements: A*): Many[A] = {
if(elements.length == 1) Const(elements(0), Empty)
else Const(elements(0), apply(elements.drop(1) : _*))
}
}
sealed trait Many[+A] {
def andThen[B](f: A => Many[B]): Many[B]
def within[B](f: A => B): Many[B]
}
case class Const[+A](head: A, tail: Many[A]) extends Many[A] {
def andThen[B](f: A => Many[B]): Many[B] = ...
def within[B](f: A => B): Many[B] = ...
}
case class Const[+A](head: A, tail: Many[A]) extends Many[A] {
def andThen[B](f: A => Many[B]): Many[B] = ...
def within[B](f: A => B): Many[B] = Const(f(head), tail.within(f))
}
case class Const[+A](head: A, tail: Many[A]) extends Many[A] {
def andThen[B](f: A => Many[B]): Many[B] =
concat( f(head), tail.andThen(f) )
def within[B](f: A => B): Many[B] = Const(f(head), tail.within(f))
}
case class Const[+A](head: A, tail: Many[A]) extends Many[A] {
def andThen[B](f: A => Many[B]): Many[B] =
concat( f(head), tail.andThen(f) )
private def concat[A](first: Many[A],
second: Many[A]): Many[A] = { … }
def within[B](f: A => B): Many[B] = Const(f(head), tail.within(f))
}
case class Const[+A](head: A, tail: Many[A]) extends Many[A] {
def andThen[B](f: A => Many[B]): Many[B] =
concat( f(head), tail.andThen(f) )
private def concat[A](first: Many[A],
second: Many[A]): Many[A] = { … }
def within[B](f: A => B): Many[B] = Const(f(head), tail.within(f))
}
case object Empty extends Many[Nothing] {
def andThen[B](f: Nothing => Many[B]): Many[B] = this
def within[B](f: Nothing => B): Many[B] = this
}
val blackPearl = Ship("Black Pearl", Hold(Empty))
val whitePearl = Ship("White Pearl",
Hold(Many(Barrel(20), Barrel(10))))
val jack = Pirate("Captain Jack Sparrow", Many(blackPearl, whitePearl))
val blackPearl = Ship("Black Pearl", Hold(Empty))
val whitePearl = Ship("White Pearl",
Hold(Many(Barrel(20), Barrel(10))))
val jack = Pirate("Captain Jack Sparrow", Many(blackPearl, whitePearl))
jack.ships.andThen(ship => ship.hold.barrel)
.within(barrel => barrel.amount)
val blackPearl = Ship("Black Pearl", Hold(Empty))
val whitePearl = Ship("White Pearl",
Hold(Many(Barrel(20), Barrel(10))))
val jack = Pirate("Captain Jack Sparrow", Many(blackPearl, whitePearl))
jack.ships.andThen(ship => ship.hold.barrel)
.within(barrel => barrel.amount)
import Magic._
val amounts = for {
ship <- jack.ships
barrel <- ship.hold.barrel
} yield (barrel.amount)
val blackPearl = Ship("Black Pearl", Hold(Empty))
val whitePearl = Ship("White Pearl",
Hold(Many(Barrel(20), Barrel(10))))
val jack = Pirate("Captain Jack Sparrow", Many(blackPearl, whitePearl))
jack.ships.andThen(ship => ship.hold.barrel)
.within(barrel => barrel.amount)
import Magic._
val amounts = for {
ship <- jack.ships
barrel <- ship.hold.barrel
} yield (barrel.amount)
val blackPearl = Ship("Black Pearl", Hold(Empty))
val whitePearl = Ship("White Pearl",
Hold(Many(Barrel(20), Barrel(10))))
val jack = Pirate("Captain Jack Sparrow", Many(blackPearl, whitePearl))
jack.ships.andThen(ship => ship.hold.barrel)
.within(barrel => barrel.amount)
import Magic._
val amounts = for {
ship <- jack.ships
barrel <- ship.hold.barrel
} yield (barrel.amount)
val blackPearl = Ship("Black Pearl", Hold(Empty))
val whitePearl = Ship("White Pearl",
Hold(Many(Barrel(20), Barrel(10))))
val jack = Pirate("Captain Jack Sparrow", Many(blackPearl, whitePearl))
jack.ships.andThen(ship => ship.hold.barrel)
.within(barrel => barrel.amount)
import Magic._
val amounts = for {
ship <- jack.ships
barrel <- ship.hold.barrel
} yield (barrel.amount)
val blackPearl = Ship("Black Pearl", Hold(Empty))
val whitePearl = Ship("White Pearl",
Hold(Many(Barrel(20), Barrel(10))))
val jack = Pirate("Captain Jack Sparrow", Many(blackPearl, whitePearl))
jack.ships.andThen(ship => ship.hold.barrel)
.within(barrel => barrel.amount)
import Magic._
val amounts = for {
ship <- jack.ships
barrel <- ship.hold.barrel
} yield (barrel.amount)
val blackPearl = Ship("Black Pearl", Hold(Empty))
val whitePearl = Ship("White Pearl",
Hold(Many(Barrel(20), Barrel(10))))
val jack = Pirate("Captain Jack Sparrow", Many(blackPearl, whitePearl))
jack.ships.andThen(ship => ship.hold.barrel)
.within(barrel => barrel.amount)
import Magic._
val amounts = for {
ship <- jack.ships
barrel <- ship.hold.barrel
} yield (barrel.amount)
val blackPearl = Ship("Black Pearl", Hold(Empty))
val whitePearl = Ship("White Pearl",
Hold(Many(Barrel(20), Barrel(10))))
val jack = Pirate("Captain Jack Sparrow", Many(blackPearl, whitePearl))
jack.ships.andThen(ship => ship.hold.barrel)
.within(barrel => barrel.amount)
import Magic._
val amounts = for {
ship <- jack.ships
barrel <- ship.hold.barrel
} yield (barrel.amount)
println(amounts) // Const(20,Const(10,Empty))
● We've seen two wrapping classes.
● We've seen two wrapping classes.
● All have andThen method.
● We've seen two wrapping classes.
● All have andThen method.
● Each can take a function that can be called
on a value wrapped by the structure (which
can be empty or there can be multiple
instances of it).
Both were examples of a
Monad!
WTF Monad?
Monad is a container, a box.
Monad is a container, a box.
A box in which we can store
an object. Wrap it.
Monad is a container, a box.
A box in which we can store
an object, a value.
A box that has a common
interface, which allows us to
do one thing: connect
sequence of operations on the
content of the box.
Monad is a container, a box.
A box in which we can store
an object, a value.
A box that has a common
interface, which allows us to
do one thing: connect
sequence of operations on the
content of the box.
andThen means "do the next
things".
Monad is a container, a box.
A box in which we can store
an object, a value.
A box that has a common
interface, which allows us to
do one thing: connect
sequence of operations on the
content of the box.
andThen means "do the next
things".
But the way it is done depends
on a box.
Monad is a container, a box.
A box in which we can store
an object, a value.
A box that has a common
interface, which allows us to
do one thing: connect
sequence of operations on the
content of the box.
andThen means "do the next
things".
But the way it is done depends
on a box.
Monad is a container, a box.
A box in which we can store
an object, a value.
A box that has a common
interface, which allows us to
do one thing: connect
sequence of operations on the
content of the box.
andThen means "do the next
things".
But the way it is done depends
on a box.
Monad is a container, a box.
A box in which we can store
an object, a value.
A box that has a common
interface, which allows us to
do one thing: connect
sequence of operations on the
content of the box.
andThen means "do the next
things".
But the way it is done depends
on a box.
Monad is an abstract data type.
Monad is an abstract data type.
It has two operators: andThen and unit
Monad is an abstract data type.
It has two operators: andThen and unit
object Maybe {
def apply[A](value: A) = Some(value)
}
Monad is an abstract data type.
It has two operators: andThen and unit
Monad is an abstract data type.
It has two operators: andThen and unit
Monad is an abstract data type.
It has two operators: andThen and unit
bind (>>=) // formal name
Monad is an abstract data type.
It has two operators: andThen and unit
bind (>>=) // formal name
flatMap // in Scala
def partnerLookup(geek: Geek): Maybe[String] = {
import Magic._
for {
p <- geek.partner
wp <- p.workPlace
s <- wp.street
} yield (s)
}
def partnerLookup(geek: Geek): Maybe[String] = {
import Magic._
for {
p <- geek.partner
wp <- p.workPlace
s <- wp.street
} yield (s)
}
object Magic {
case class RichMaybe[A](m: Maybe[A]) {
def flatMap[B](f: A => Maybe[B]): Maybe[B] = m.andThen(f)
def map[B](f: A => B): Maybe[B] = m.within(f)
}
implicit def enrich[A](m: Maybe[A]) = RichMaybe(m)
}
def partnerLookup(geek: Geek): Maybe[String] = {
import Magic._
for {
p <- geek.partner
wp <- p.workPlace
s <- wp.street
} yield (s)
}
def partnerLookup(geek: Geek): Maybe[String] = {
for {
p <- geek.partner
wp <- p.workPlace
s <- wp.street
} yield (s)
}
Monad is an abstract data type.
It has two operators: bind and unit
Monad is an abstract data type.
It has two operators: bind and unit
Those operators must follow certain rules:
1. associativity
2. left identity
3. right identity
1. associativity
2. left identity
3. right identity
unit acts approximately as a
neutral element of bind
1. associativity
2. left identity
3. right identity
unit acts approximately as a
neutral element of bind
unit allows us to put value
into the box without
damaging it
1. associativity
2. left identity
3. right identity
unit acts approximately as a
neutral element of bind
unit(x).flatMap(f) == f(x)
m.flatMap(unit) == m
unit allows us to put value
into the box without
damaging it
1. associativity
2. left identity
3. right identity
“Binding two functions in
succession is the same as
binding one function that can
be determined from them”
1. associativity
2. left identity
3. right identity
m.flatMap{f(_)}.flatMap.{g(_)}
==
m.flatMap{f(_).flatMap.{g(_)}}
“Binding two functions in
succession is the same as
binding one function that can
be determined from them”
WTF Monad?
case class User(id: Long, fn: String, ln: String)
class Cache { def check(id: Long): Option[User] = ??? }
case class UserRepo(cache: Cache) {
def retrieve(id: Long): User = ???
}
class UserFinder(cache: Cache, repo: UserRepo) {
def findUser(id: Long): User = {
val maybeUser: Option[User] = cache.check(id)
if (maybeUser.isDefined) {
maybeUser.get
} else {
val user: User = repo.retrieve(id)
cache.insert(user.id, user)
}
}
}
case class User(id: Long, fn: String, ln: String)
case class User(id: Long, fn: String, ln: String)
class Cache {}
case class User(id: Long, fn: String, ln: String)
class Cache {}
def check(id: Long)(cache: Cache): (Cache, Option[User]) = ...
case class User(id: Long, fn: String, ln: String)
class Cache {}
def check(id: Long)(cache: Cache): (Cache, Option[User]) = ...
def retrieve(id: Long)(cache: Cache): (Cache, User) = ...
case class User(id: Long, fn: String, ln: String)
class Cache {}
def check(id: Long)(cache: Cache): (Cache, Option[User]) = ...
def retrieve(id: Long)(cache: Cache): (Cache, User) = ...
def findUser(id: Long)(cache: Cache): (Cache, User) = {
}
}
case class User(id: Long, fn: String, ln: String)
class Cache {}
def check(id: Long)(cache: Cache): (Cache, Option[User]) = ...
def retrieve(id: Long)(cache: Cache): (Cache, User) = ...
def findUser(id: Long)(cache: Cache): (Cache, User) = {
val (c, mu) = check(id)(cache)
}
}
case class User(id: Long, fn: String, ln: String)
class Cache {}
def check(id: Long)(cache: Cache): (Cache, Option[User]) = ...
def retrieve(id: Long)(cache: Cache): (Cache, User) = ...
def findUser(id: Long)(cache: Cache): (Cache, User) = {
val (c, mu) = check(id)(cache)
mu match {
case Some(u) => (c, u)
}
}
}
case class User(id: Long, fn: String, ln: String)
class Cache {}
def check(id: Long)(cache: Cache): (Cache, Option[User]) = ...
def retrieve(id: Long)(cache: Cache): (Cache, User) = ...
def findUser(id: Long)(cache: Cache): (Cache, User) = {
val (c, mu) = check(id)(cache)
mu match {
case Some(u) => (c, u)
case None => retrieve(id)(c)
}
}
}
case class User(id: Long, fn: String, ln: String)
class Cache {}
def check(id: Long)(cache: Cache): (Cache, Option[User]) = ...
def retrieve(id: Long)(cache: Cache): (Cache, User) = ...
def findUser(id: Long)(cache: Cache): (Cache, User) = {
val (c, mu) = check(id)(cache)
mu match {
case Some(u) => (c, u)
case None => retrieve(id)(c)
}
}
}
S => (S, A)
State[S, A]
S => (S, A)
State[S, A]
S => (S, A)
.run(S)
State[S, A]
S => (S, A)
.map(A => B): State[S, B]
State[S, A]
S => (S, A)
.flatMap(A => State[S, B]): State[S,B]
object State {
def apply[S, A] (f: S => (S,A)): State[S, A] =
}
object State {
def apply[S, A] (f: S => (S,A)): State[S, A] =
new State[S, A] {
def run(s: S) = f(s)
}
}
object State {
def apply[S, A] (f: S => (S,A)): State[S, A] =
new State[S, A] {
def run(s: S) = f(s)
}
}
def check(id: String) = State[Cache, Option[User]].apply {
}
object State {
def apply[S, A] (f: S => (S,A)): State[S, A] =
new State[S, A] {
def run(s: S) = f(s)
}
}
def check(id: String) = State[Cache, Option[User]].apply {
(c: Cache) => (c, c.get(id))
}
object State {
def apply[S, A] (f: S => (S,A)): State[S, A] =
new State[S, A] {
def run(s: S) = f(s(
}
}
def check(id: String) = State[Cache, Option[User]]{
(c: Cache) => (c, c.get(id))
}
trait State[S, +A] {
}
trait State[S, +A] {
def run(initial: S): (S, A)
}
trait State[S, +A] {
def run(initial: S): (S, A)
def map[B](f: A => B): State[S, B] =
}
}
trait State[S, +A] {
def run(initial: S): (S, A)
def map[B](f: A => B): State[S, B] =
State {
}
}
}
trait State[S, +A] {
def run(initial: S): (S, A)
def map[B](f: A => B): State[S, B] =
State { s0 =>
(_, _ )
}
}
}
trait State[S, +A] {
def run(initial: S): (S, A)
def map[B](f: A => B): State[S, B] =
State { s0 =>
(_, f(a))
}
}
}
trait State[S, +A] {
def run(initial: S): (S, A)
def map[B](f: A => B): State[S, B] =
State { s0 =>
val (s, a) = run(s0)
(_, f(a))
}
}
}
trait State[S, +A] {
def run(initial: S): (S, A)
def map[B](f: A => B): State[S, B] =
State { s0 =>
val (s, a) = run(s0)
(s, f(a))
}
}
}
trait State[S, +A] {
def run(initial: S): (S, A)
def map[B](f: A => B): State[S, B] =
State { s0 =>
val (s, a) = run(s0)
(s, f(a))
}
}
def flatMap[B](f: A => State[S,B]): State[S, B] =
}
}
trait State[S, +A] {
def run(initial: S): (S, A)
def map[B](f: A => B): State[S, B] =
State { s0 =>
val (s, a) = run(s0)
(s, f(a))
}
}
def flatMap[B](f: A => State[S,B]): State[S, B] =
f(a)
}
}
trait State[S, +A] {
def run(initial: S): (S, A)
def map[B](f: A => B): State[S, B] =
State { s0 =>
val (s, a) = run(s0)
(s, f(a))
}
}
def flatMap[B](f: A => State[S,B]): State[S, B] =
val (s, a) = run(s0)
f(a)
}
}
trait State[S, +A] {
def run(initial: S): (S, A)
def map[B](f: A => B): State[S, B] =
State { s0 =>
val (s, a) = run(s0)
(s, f(a))
}
}
def flatMap[B](f: A => State[S,B]): State[S, B] =
State { s0 =>
val (s, a) = run(s0)
f(a)
}
}
}
trait State[S, +A] {
def run(initial: S): (S, A)
def map[B](f: A => B): State[S, B] =
State { s0 =>
val (s, a) = run(s0)
(s, f(a))
}
}
def flatMap[B](f: A => State[S,B]): State[S, B] =
State { s0 =>
val (s, a) = run(s0)
f(a).run(s)
}
}
}
case class User(id: Long, fn: String, ln: String)
class Cache {}
def check(id: Long)(cache: Cache): (Cache, Option[User]) = ???
def retrieve(id: Long)(cache: Cache): (Cache, User) = ???
def findUser(id: Long)(cache: Cache): (Cache, User) = {
val (c, mu) = check(id)(cache)
mu match {
case Some(u) => (c, u)
case None => retrieve(id)(c)
}
}
}
case class User(id: Long, fn: String, ln: String)
class Cache {}
def check(id: Long)(cache: Cache): (Cache, Option[User]) = ???
def retrieve(id: Long)(cache: Cache): (Cache, User) = ???
def findUser(id: Long)(cache: Cache): (Cache, User) = {
val (c, mu) = check(id)(cache)
mu match {
case Some(u) => (c, u)
case None => retrieve(id)(c)
}
}
}
case class User(id: Long, fn: String, ln: String)
class Cache {}
def check(id: Long): State[Cache, Option[User]] = ???
def retrieve(id: Long): State[Cache, User] = ???
def findUser(id: Long): State[Cache, User] = {
for {
maybeUser <- check(id)
user <- maybeUser match {
case Some(u) => State { c => (c, u)}
case None => retrieve(id)
}
} yield (user)
}
Event sourcing
Event Sourcing
Event Sourcing
driven by business
Bloggers Conf App
Bloggers Conf App
● Can create an account
Bloggers Conf App
● Can create an account
● List all bloggers already using the app
Bloggers Conf App
● Can create an account
● List all bloggers already using the app
● Mark/unmark other blogger as a friend
Bloggers Conf App
● Can create an account
● List all bloggers already using the app
● Mark/unmark other blogger as a friend
● Mark/unmark other blogger as an enemy
Bloggers Conf App
● Can create an account
● List all bloggers already using the app
● Mark/unmark other blogger as a friend
● Mark/unmark other blogger as an enemy
● Deactivate its account
Bloggers Conf App
Bloggers
id first_name last_name active
1 Jan Kowalski T
2 Krystian Nowak T
3 Malgorzata Kucharska T
Bloggers Conf App
Bloggers
id first_name last_name active
1 Jan Kowalski T
2 Krystian Nowak T
3 Malgorzata Kucharska T
Friends
id friend_id
3 1
Bloggers Conf App
Bloggers
id first_name last_name active
1 Jan Kowalski T
2 Krystian Nowak T
3 Malgorzata Kucharska T
Friends
id friend_id
3 1
Enemies
id enemy_id
3 2
Structure is not that
important
Structure is not that
important
Changes more often than behaviour
Start thinking about facts
occurring
Start thinking about facts
occurring
Derive structure from them
Blogger
Account
Created
(id=3)
Blogger
Account
Created
(id=3)
Befriended
Blogger
id=1
Blogger
Account
Created
(id=3)
Befriended
Blogger
id=1
Made
Enemy of
Blogger
id=2
Blogger
Account
Created
(id=3)
Befriended
Blogger
id=1
Made
Enemy of
Blogger
id=2
Bloggers
id first_name last_name active
1 Jan Kowalski T
2 Krystian Nowak T
Friends
id friend_id
Enemies
id enemy_id
Blogger
Account
Created
(id=3)
Befriended
Blogger
id=1
Made
Enemy of
Blogger
id=2
Bloggers
id first_name last_name active
1 Jan Kowalski T
2 Krystian Nowak T
3 Malgorzata Kucharska T
Friends
id friend_id
Enemies
id enemy_id
Blogger
Account
Created
(id=3)
Befriended
Blogger
id=1
Made
Enemy of
Blogger
id=2
Bloggers
id first_name last_name active
1 Jan Kowalski T
2 Krystian Nowak T
3 Malgorzata Kucharska T
Friends
id friend_id
3 1
Enemies
id enemy_id
Blogger
Account
Created
(id=3)
Befriended
Blogger
id=1
Made
Enemy of
Blogger
id=2
Bloggers
id first_name last_name active
1 Jan Kowalski T
2 Krystian Nowak T
3 Malgorzata Kucharska T
Friends
id friend_id
3 1
Enemies
id enemy_id
3 2
Blogger
Account
Created
(id=3)
Befriended
Blogger
id=1
Made
Enemy of
Blogger
id=2
Blogger
Account
Created
(id=3)
Befriended
Blogger
id=1
Made
Enemy of
Blogger
id=2
Befriended
Blogger
id=2
Unfriended
Blogger
id=2
Blogger
Account
Created
(id=3)
Befriended
Blogger
id=1
Made
Enemy of
Blogger
id=2
Event Sourcing
driven by business
Event Sourcing
The only model that does not lose data
“Enemy of my enemy is
my friend”
Bloggers
id first_name last_name active
1 Jan Kowalski T
2 Krystian Nowak T
3 Malgorzata Kucharska T
Friends
id friend_id
3 1
Enemies
id enemy_id
3 2
Bloggers
id first_name last_name active
1 Jan Kowalski T
2 Krystian Nowak T
3 Malgorzata Kucharska T
4 Tomasz Młynarski T
Friends
id friend_id
3 1
Enemies
id enemy_id
3 2
4 2
Bloggers
id first_name last_name active
1 Jan Kowalski T
2 Krystian Nowak T
3 Malgorzata Kucharska T
4 Tomasz Młynarski T
5 Monika Jagoda T
Friends
id friend_id
3 1
Enemies
id enemy_id
3 2
4 2
2 5
id = 3
id = 2
id = 1
id = 4
id = 5
id = 6
id = 3
id = 2
id = 1
id = 4
id = 5
id = 6
id = 3
id = 2
id = 1
id = 4
id = 5
id = 6
id = 3
id = 2
id = 1
id = 4
id = 5
id = 6
id = 3
id = 2
id = 1
id = 4
id = 5
id = 6
id = 3
id = 2
id = 1
id = 4
id = 5
id = 6
id = 3
id = 2
id = 1
id = 4
id = 5
id = 6
id = 3
id = 2
id = 1
id = 4
id = 5
id = 6
Benefits of Event Sourcing
Benefits of Event Sourcing
● Ability to go in time and figure out exactly what have
happened
Benefits of Event Sourcing
● Ability to go in time and figure out exactly what have
happened
● Scientific measurements over time, compare time
periods
Benefits of Event Sourcing
● Ability to go in time and figure out exactly what have
happened
● Scientific measurements over time, compare time
periods
● Built-in audit log
Benefits of Event Sourcing
● Ability to go in time and figure out exactly what have
happened
● Scientific measurements over time, compare time
periods
● Built-in audit log
● Enables temporal querying
Benefits of Event Sourcing
● Ability to go in time and figure out exactly what have
happened
● Scientific measurements over time, compare time
periods
● Built-in audit log
● Enables temporal querying
● Fits well with machine learning
Benefits of Event Sourcing
● Ability to go in time and figure out exactly what have
happened
● Scientific measurements over time, compare time
periods
● Built-in audit log
● Enables temporal querying
● Fits well with machine learning
● Preserves history - question not yet asked
Benefits of Event Sourcing
● Ability to go in time and figure out exactly what have
happened
● Scientific measurements over time, compare time
periods
● Built-in audit log
● Enables temporal querying
● Fits well with machine learning
● Preserves history - question not yet asked
● Writing regression tests is easy
Benefits of Event Sourcing
● Ability to go in time and figure out exactly what have
happened
● Scientific measurements over time, compare time
periods
● Built-in audit log
● Enables temporal querying
● Fits well with machine learning
● Preserves history - question not yet asked
● Writing regression tests is easy
● Polyglot data
Drawbacks of Event Sourcing
Drawbacks of Event Sourcing
● Historical record of your bad decisions
Drawbacks of Event Sourcing
● Historical record of your bad decisions
● Handling event duplicates
Drawbacks of Event Sourcing
● Historical record of your bad decisions
● Handling event duplicates
● Data eventually consistent
How to implement it?
Events vs Commands
Journal
Journal
Journal
val id = “”
val firstName: String = “”
val lastName: String = “”
val friends: List[String] =
List()
Journal
val id = “”
val firstName: String = “”
val lastName: String = “”
val friends: List[String] =
List()
Journal
val id = “”
val firstName: String = “”
val lastName: String = “”
val friends: List[String] =
List()
Journal
val id = “”
val firstName: String = “”
val lastName: String = “”
val friends: List[String] =
List()
Initialized(“1”, “Jan”, “Kowalski”
Journal
val id = “”
val firstName: String = “”
val lastName: String = “”
val friends: List[String] =
List()
Initialized(“1”, “Jan”, “Kowalski”
Journal
val id = “1”
val firstName: String =
“Jan”
val lastName: String =
“Kowalski”
val friends: List[String] =
List()
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] =
List()
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] =
List()
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] =
List()
Befriended(“10”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] =
List()
Befriended(“10”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] =
List(“10”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] =
List(“10”, “31”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”, “31”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”, “31”)
Befriend(“31”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”, “31”)
Befriend(“31”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”, “31”)
Befriend(“31”)
validation
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”, “31”)
Befriend(“31”)
validation
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”, “31”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”, “31”)
Befriend(“34”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”, “31”)
Befriend(“34”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”, “31”)
Befriend(“34”)
validation
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”, “31”)
Befriend(“34”)
validation
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”, “31”)
Befriend(“34”)
Befriended(“34”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”, “31”)
Befriend(“34”)
Befriended(“34”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”, “31”)
Befriend(“34”)
Befriended(“34”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”, “31”)
Befriend(“34”)
Befriended(“34”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”, “31”)
Befriend(“34”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”, “31”)
Befriend(“34”)
ACK
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] =
List(“10”, “31”, “34”)
Befriend(“34”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] =
List(“10”, “31”, “34”)
Let’s see some code!
What we are missing?
What we are missing?
1. Read-model
What we are missing?
1. Read-model
2. Validation
And that’s all folks!
Paweł Szulc
Paweł Szulc
http://rabbitonweb.com
Paweł Szulc
http://rabbitonweb.com
@rabbitonweb
Paweł Szulc
http://rabbitonweb.com
@rabbitonweb
https://github.com/rabbitonweb/
Thank you!

Mais conteúdo relacionado

Mais procurados

The Ring programming language version 1.10 book - Part 30 of 212
The Ring programming language version 1.10 book - Part 30 of 212The Ring programming language version 1.10 book - Part 30 of 212
The Ring programming language version 1.10 book - Part 30 of 212Mahmoud Samir Fayed
 
Stanfy MadCode Meetup #9: Functional Programming 101 with Swift
Stanfy MadCode Meetup #9: Functional Programming 101 with SwiftStanfy MadCode Meetup #9: Functional Programming 101 with Swift
Stanfy MadCode Meetup #9: Functional Programming 101 with SwiftStanfy
 
Kotlin Advanced - Apalon Kotlin Sprint Part 3
Kotlin Advanced - Apalon Kotlin Sprint Part 3Kotlin Advanced - Apalon Kotlin Sprint Part 3
Kotlin Advanced - Apalon Kotlin Sprint Part 3Kirill Rozov
 
Functional Patterns for the non-mathematician
Functional Patterns for the non-mathematicianFunctional Patterns for the non-mathematician
Functional Patterns for the non-mathematicianBrian Lonsdorf
 
The Ring programming language version 1.7 book - Part 26 of 196
The Ring programming language version 1.7 book - Part 26 of 196The Ring programming language version 1.7 book - Part 26 of 196
The Ring programming language version 1.7 book - Part 26 of 196Mahmoud Samir Fayed
 
Nik Graf - Get started with Reason and ReasonReact
Nik Graf - Get started with Reason and ReasonReactNik Graf - Get started with Reason and ReasonReact
Nik Graf - Get started with Reason and ReasonReactOdessaJS Conf
 
Why The Free Monad isn't Free
Why The Free Monad isn't FreeWhy The Free Monad isn't Free
Why The Free Monad isn't FreeKelley Robinson
 
Functional Programming for OO Programmers (part 2)
Functional Programming for OO Programmers (part 2)Functional Programming for OO Programmers (part 2)
Functional Programming for OO Programmers (part 2)Calvin Cheng
 
Type classes 101 - classification beyond inheritance
Type classes 101 - classification beyond inheritanceType classes 101 - classification beyond inheritance
Type classes 101 - classification beyond inheritanceAlexey Raga
 
Functional Pe(a)rls - the Purely Functional Datastructures edition
Functional Pe(a)rls - the Purely Functional Datastructures editionFunctional Pe(a)rls - the Purely Functional Datastructures edition
Functional Pe(a)rls - the Purely Functional Datastructures editionosfameron
 
Template Haskell とか
Template Haskell とかTemplate Haskell とか
Template Haskell とかHiromi Ishii
 
Programming Haskell Chapter8
Programming Haskell Chapter8Programming Haskell Chapter8
Programming Haskell Chapter8Kousuke Ruichi
 
Introduction to Functional Programming with Scala
Introduction to Functional Programming with ScalaIntroduction to Functional Programming with Scala
Introduction to Functional Programming with ScalaDaniel Cukier
 

Mais procurados (20)

Kotlin standard
Kotlin standardKotlin standard
Kotlin standard
 
The Ring programming language version 1.10 book - Part 30 of 212
The Ring programming language version 1.10 book - Part 30 of 212The Ring programming language version 1.10 book - Part 30 of 212
The Ring programming language version 1.10 book - Part 30 of 212
 
Stanfy MadCode Meetup #9: Functional Programming 101 with Swift
Stanfy MadCode Meetup #9: Functional Programming 101 with SwiftStanfy MadCode Meetup #9: Functional Programming 101 with Swift
Stanfy MadCode Meetup #9: Functional Programming 101 with Swift
 
20170509 rand db_lesugent
20170509 rand db_lesugent20170509 rand db_lesugent
20170509 rand db_lesugent
 
Kotlin Advanced - Apalon Kotlin Sprint Part 3
Kotlin Advanced - Apalon Kotlin Sprint Part 3Kotlin Advanced - Apalon Kotlin Sprint Part 3
Kotlin Advanced - Apalon Kotlin Sprint Part 3
 
P3 2017 python_regexes
P3 2017 python_regexesP3 2017 python_regexes
P3 2017 python_regexes
 
Functional Patterns for the non-mathematician
Functional Patterns for the non-mathematicianFunctional Patterns for the non-mathematician
Functional Patterns for the non-mathematician
 
Millionways
MillionwaysMillionways
Millionways
 
Fp java8
Fp java8Fp java8
Fp java8
 
Hammurabi
HammurabiHammurabi
Hammurabi
 
The Ring programming language version 1.7 book - Part 26 of 196
The Ring programming language version 1.7 book - Part 26 of 196The Ring programming language version 1.7 book - Part 26 of 196
The Ring programming language version 1.7 book - Part 26 of 196
 
Nik Graf - Get started with Reason and ReasonReact
Nik Graf - Get started with Reason and ReasonReactNik Graf - Get started with Reason and ReasonReact
Nik Graf - Get started with Reason and ReasonReact
 
Oh Composable World!
Oh Composable World!Oh Composable World!
Oh Composable World!
 
Why The Free Monad isn't Free
Why The Free Monad isn't FreeWhy The Free Monad isn't Free
Why The Free Monad isn't Free
 
Functional Programming for OO Programmers (part 2)
Functional Programming for OO Programmers (part 2)Functional Programming for OO Programmers (part 2)
Functional Programming for OO Programmers (part 2)
 
Type classes 101 - classification beyond inheritance
Type classes 101 - classification beyond inheritanceType classes 101 - classification beyond inheritance
Type classes 101 - classification beyond inheritance
 
Functional Pe(a)rls - the Purely Functional Datastructures edition
Functional Pe(a)rls - the Purely Functional Datastructures editionFunctional Pe(a)rls - the Purely Functional Datastructures edition
Functional Pe(a)rls - the Purely Functional Datastructures edition
 
Template Haskell とか
Template Haskell とかTemplate Haskell とか
Template Haskell とか
 
Programming Haskell Chapter8
Programming Haskell Chapter8Programming Haskell Chapter8
Programming Haskell Chapter8
 
Introduction to Functional Programming with Scala
Introduction to Functional Programming with ScalaIntroduction to Functional Programming with Scala
Introduction to Functional Programming with Scala
 

Semelhante a JDD2015: Functional programing and Event Sourcing - a pair made in heaven - extended, 2 hours long brainwash - Paweł Szulc

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
 
Purely Functional Data Structures in Scala
Purely Functional Data Structures in ScalaPurely Functional Data Structures in Scala
Purely Functional Data Structures in ScalaVladimir Kostyukov
 
Scala - where objects and functions meet
Scala - where objects and functions meetScala - where objects and functions meet
Scala - where objects and functions meetMario Fusco
 
Kotlin collections
Kotlin collectionsKotlin collections
Kotlin collectionsMyeongin Woo
 
Делаем пользовательское Api на базе Shapeless
Делаем пользовательское Api на базе ShapelessДелаем пользовательское Api на базе Shapeless
Делаем пользовательское Api на базе ShapelessВадим Челышов
 
FP in scalaで鍛える関数型脳
FP in scalaで鍛える関数型脳FP in scalaで鍛える関数型脳
FP in scalaで鍛える関数型脳Yuri Inoue
 
[Expert Fridays] Александр Чичигин - Как перестать бояться и полюбить COQ
[Expert Fridays] Александр Чичигин - Как перестать бояться и полюбить COQ[Expert Fridays] Александр Чичигин - Как перестать бояться и полюбить COQ
[Expert Fridays] Александр Чичигин - Как перестать бояться и полюбить COQProvectus
 
Modular Module Systems
Modular Module SystemsModular Module Systems
Modular Module Systemsleague
 
Swift 함수 커링 사용하기
Swift 함수 커링 사용하기Swift 함수 커링 사용하기
Swift 함수 커링 사용하기진성 오
 
Swift Rocks #2: Going functional
Swift Rocks #2: Going functionalSwift Rocks #2: Going functional
Swift Rocks #2: Going functionalHackraft
 
Algebraic Data Types and Origami Patterns
Algebraic Data Types and Origami PatternsAlgebraic Data Types and Origami Patterns
Algebraic Data Types and Origami PatternsVasil Remeniuk
 
lab08build.bat@echo offclsset DRIVE_LETTER=1s.docx
lab08build.bat@echo offclsset DRIVE_LETTER=1s.docxlab08build.bat@echo offclsset DRIVE_LETTER=1s.docx
lab08build.bat@echo offclsset DRIVE_LETTER=1s.docxDIPESH30
 
仕事で使うF#
仕事で使うF#仕事で使うF#
仕事で使うF#bleis tift
 
Functional programming with_scala
Functional programming with_scalaFunctional programming with_scala
Functional programming with_scalaRaymond Tay
 

Semelhante a JDD2015: Functional programing and Event Sourcing - a pair made in heaven - extended, 2 hours long brainwash - Paweł Szulc (20)

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
 
Scala best practices
Scala best practicesScala best practices
Scala best practices
 
Purely Functional Data Structures in Scala
Purely Functional Data Structures in ScalaPurely Functional Data Structures in Scala
Purely Functional Data Structures in Scala
 
Scala - where objects and functions meet
Scala - where objects and functions meetScala - where objects and functions meet
Scala - where objects and functions meet
 
Kotlin collections
Kotlin collectionsKotlin collections
Kotlin collections
 
List out of lambda
List out of lambdaList out of lambda
List out of lambda
 
Functional programming in scala
Functional programming in scalaFunctional programming in scala
Functional programming in scala
 
Делаем пользовательское Api на базе Shapeless
Делаем пользовательское Api на базе ShapelessДелаем пользовательское Api на базе Shapeless
Делаем пользовательское Api на базе Shapeless
 
Python Lecture 11
Python Lecture 11Python Lecture 11
Python Lecture 11
 
FP in scalaで鍛える関数型脳
FP in scalaで鍛える関数型脳FP in scalaで鍛える関数型脳
FP in scalaで鍛える関数型脳
 
[Expert Fridays] Александр Чичигин - Как перестать бояться и полюбить COQ
[Expert Fridays] Александр Чичигин - Как перестать бояться и полюбить COQ[Expert Fridays] Александр Чичигин - Как перестать бояться и полюбить COQ
[Expert Fridays] Александр Чичигин - Как перестать бояться и полюбить COQ
 
Modular Module Systems
Modular Module SystemsModular Module Systems
Modular Module Systems
 
Map, Reduce and Filter in Swift
Map, Reduce and Filter in SwiftMap, Reduce and Filter in Swift
Map, Reduce and Filter in Swift
 
Swift 함수 커링 사용하기
Swift 함수 커링 사용하기Swift 함수 커링 사용하기
Swift 함수 커링 사용하기
 
Swift Rocks #2: Going functional
Swift Rocks #2: Going functionalSwift Rocks #2: Going functional
Swift Rocks #2: Going functional
 
Algebraic Data Types and Origami Patterns
Algebraic Data Types and Origami PatternsAlgebraic Data Types and Origami Patterns
Algebraic Data Types and Origami Patterns
 
lab08build.bat@echo offclsset DRIVE_LETTER=1s.docx
lab08build.bat@echo offclsset DRIVE_LETTER=1s.docxlab08build.bat@echo offclsset DRIVE_LETTER=1s.docx
lab08build.bat@echo offclsset DRIVE_LETTER=1s.docx
 
仕事で使うF#
仕事で使うF#仕事で使うF#
仕事で使うF#
 
Functional programming with_scala
Functional programming with_scalaFunctional programming with_scala
Functional programming with_scala
 
Zippers
ZippersZippers
Zippers
 

Último

WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...WSO2
 
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...SelfMade bd
 
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisamasabamasaba
 
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...WSO2
 
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...Jittipong Loespradit
 
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...masabamasaba
 
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension AidPhilip Schwarz
 
%in Benoni+277-882-255-28 abortion pills for sale in Benoni
%in Benoni+277-882-255-28 abortion pills for sale in Benoni%in Benoni+277-882-255-28 abortion pills for sale in Benoni
%in Benoni+277-882-255-28 abortion pills for sale in Benonimasabamasaba
 
WSO2CON 2024 Slides - Open Source to SaaS
WSO2CON 2024 Slides - Open Source to SaaSWSO2CON 2024 Slides - Open Source to SaaS
WSO2CON 2024 Slides - Open Source to SaaSWSO2
 
AI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplateAI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplatePresentation.STUDIO
 
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...WSO2
 
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...chiefasafspells
 
WSO2Con204 - Hard Rock Presentation - Keynote
WSO2Con204 - Hard Rock Presentation - KeynoteWSO2Con204 - Hard Rock Presentation - Keynote
WSO2Con204 - Hard Rock Presentation - KeynoteWSO2
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...masabamasaba
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park masabamasaba
 
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...masabamasaba
 
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrainmasabamasaba
 
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyviewmasabamasaba
 

Último (20)

WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
 
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
 
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
 
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
 
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
 
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
 
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
 
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
 
%in Benoni+277-882-255-28 abortion pills for sale in Benoni
%in Benoni+277-882-255-28 abortion pills for sale in Benoni%in Benoni+277-882-255-28 abortion pills for sale in Benoni
%in Benoni+277-882-255-28 abortion pills for sale in Benoni
 
WSO2CON 2024 Slides - Open Source to SaaS
WSO2CON 2024 Slides - Open Source to SaaSWSO2CON 2024 Slides - Open Source to SaaS
WSO2CON 2024 Slides - Open Source to SaaS
 
AI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplateAI & Machine Learning Presentation Template
AI & Machine Learning Presentation Template
 
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
 
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
 
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
 
WSO2Con204 - Hard Rock Presentation - Keynote
WSO2Con204 - Hard Rock Presentation - KeynoteWSO2Con204 - Hard Rock Presentation - Keynote
WSO2Con204 - Hard Rock Presentation - Keynote
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
 
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
 
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
 
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
 

JDD2015: Functional programing and Event Sourcing - a pair made in heaven - extended, 2 hours long brainwash - Paweł Szulc

  • 1. Functional Programming & Event Sourcing A pair made in heaven twitter: @rabbitonweb, email: paul.szulc@gmail.com
  • 2. Functional Programming & Event Sourcing A pair made in heaven twitter: @rabbitonweb, email: paul.szulc@gmail.com2h brainwash
  • 4. ● no assignment statements
  • 5. ● no assignment statements ● no variables
  • 6. ● no assignment statements ● no variables ● once given a value, never change
  • 7. ● no assignment statements ● no variables ● once given a value, never change ● no side-effects at all
  • 8. ● no assignment statements ● no variables ● once given a value, never change ● no side-effects at all
  • 9. “The functional programmer sounds rather like a mediæval monk,
  • 10. “The functional programmer sounds rather like a mediæval monk, denying himself the pleasures of life
  • 11. “The functional programmer sounds rather like a mediæval monk, denying himself the pleasures of life in the hope that it will make him virtuous.”
  • 12. case class User(id: Long, fn: String, ln: String)
  • 13. case class User(id: Long, fn: String, ln: String) class Cache { def check(id: Long): Option[User] = ??? }
  • 14. case class User(id: Long, fn: String, ln: String) class Cache { def check(id: Long): Option[User] = ??? } case class UserRepo(cache: Cache) { def retrieve(id: Long): User = ??? }
  • 15. case class User(id: Long, fn: String, ln: String) class Cache { def check(id: Long): Option[User] = ??? } case class UserRepo(cache: Cache) { def retrieve(id: Long): User = ??? } class UserFinder(cache: Cache, repo: UserRepo) { }
  • 16. case class User(id: Long, fn: String, ln: String) class Cache { def check(id: Long): Option[User] = ??? } case class UserRepo(cache: Cache) { def retrieve(id: Long): User = ??? } class UserFinder(cache: Cache, repo: UserRepo) { def findUser(id: Long): User = { } }
  • 17. case class User(id: Long, fn: String, ln: String) class Cache { def check(id: Long): Option[User] = ??? } case class UserRepo(cache: Cache) { def retrieve(id: Long): User = ??? } class UserFinder(cache: Cache, repo: UserRepo) { def findUser(id: Long): User = { val maybeUser: Option[User] = cache.check(id) } }
  • 18. case class User(id: Long, fn: String, ln: String) class Cache { def check(id: Long): Option[User] = ??? } case class UserRepo(cache: Cache) { def retrieve(id: Long): User = ??? } class UserFinder(cache: Cache, repo: UserRepo) { def findUser(id: Long): User = { val maybeUser: Option[User] = cache.check(id) if (maybeUser.isDefined) { maybeUser.get } } }
  • 19. case class User(id: Long, fn: String, ln: String) class Cache { def check(id: Long): Option[User] = ??? } case class UserRepo(cache: Cache) { def retrieve(id: Long): User = ??? } class UserFinder(cache: Cache, repo: UserRepo) { def findUser(id: Long): User = { val maybeUser: Option[User] = cache.check(id) if (maybeUser.isDefined) { maybeUser.get } else { val user: User = repo.retrieve(id) cache.insert(user.id, user) } } }
  • 20. No concept of ‘time’
  • 24. input f(input): output output g(input): output
  • 25. input f(input): output output g(input): output output
  • 26. input f(input): output output g(input): output output
  • 27. h = g o f input f(input): output output g(input): output output
  • 29. h = g o f input f(input): output output g(input): output output
  • 30. /** * This function returns a reversed list * @param list A list to be reversed * @return A reversed list */ public List<T> reverse(List<t> list) { ??? }
  • 31.
  • 32.
  • 33. /** * This function returns a reversed list * @param list A list to be reversed * @return A reversed list */ public List<T> reverse(List<t> list) { ??? }
  • 34. /** * This function returns a reversed list * @param list A list to be reversed public List<T> reverse(List<t> list) { ??? }
  • 35. /** * This function returns a reversed list public List<T> reverse(List<t> list) { ??? }
  • 38. public List<T> reverse(List<t> list) { return list.sort(); }
  • 44. def smdfknmsdfp(a: Int): Int = a = a + 10 = 10
  • 45.
  • 47. def sum(list: List[Int]): Int = list match { case Nil => 0
  • 48. def sum(list: List[Int]): Int = list match { case Nil => 0 case num :: tail => num + sum(tail) }
  • 49. def sum(list: List[Int]): Int = list match { case Nil => 0 case num :: tail => num + sum(tail) }
  • 50. def reduce[T, K] (list: List[T]): K =
  • 51. def reduce[T, K] (list: List[T]): K = list match { case Nil => zero
  • 52. def reduce[T, K] (zero: K) (list: List[T]): K = list match { case Nil => zero
  • 53. def reduce[T, K] (zero: K) (list: List[T]): K = list match { case Nil => zero case head :: tail => op(head)(reduce(op)(zero)(tail)) }
  • 54. def reduce[T, K](op: T => K => K) (zero: K) (list: List[T]): K = list match { case Nil => zero case head :: tail => op(head)(reduce(op)(zero)(tail)) }
  • 55. def reduce[T, K](op: T => K => K) (zero: K) (list: List[T]): K = list match { case Nil => zero case head :: tail => op(head)(reduce(op)(zero)(tail)) }
  • 56. def reduce[T, K](op: T => K => K) (zero: K) (list: List[T]): K = list match { case Nil => zero case head :: tail => op(head)(reduce(op)(zero)(tail)) } def sum: List[Int] => Int =
  • 57. def reduce[T, K](op: T => K => K) (zero: K) (list: List[T]): K = list match { case Nil => zero case head :: tail => op(head)(reduce(op)(zero)(tail)) } def sum: List[Int] => Int = reduce(plus)(0)
  • 58. def reduce[T, K](op: T => K => K) (zero: K) (list: List[T]): K = list match { case Nil => zero case head :: tail => op(head)(reduce(op)(zero)(tail)) }
  • 59. def reduce[T, K](op: T => K => K) (zero: K) (list: List[T]): K = list match { case Nil => zero case head :: tail => op(head)(reduce(op)(zero)(tail)) } def product: List[Int] => Int =
  • 60. def reduce[T, K](op: T => K => K) (zero: K) (list: List[T]): K = list match { case Nil => zero case head :: tail => op(head)(reduce(op)(zero)(tail)) } def product: List[Int] => Int = reduce(multiply)(1)
  • 61. def reduce[T, K](op: T => K => K) (zero: K) (list: List[T]): K = list match { case Nil => zero case head :: tail => op(head)(reduce(op)(zero)(tail)) }
  • 62. def reduce[T, K](op: T => K => K) (zero: K) (list: List[T]): K = list match { case Nil => zero case head :: tail => op(head)(reduce(op)(zero)(tail)) } def anyTrue: List[Boolean] => Boolean =
  • 63. def reduce[T, K](op: T => K => K) (zero: K) (list: List[T]): K = list match { case Nil => zero case head :: tail => op(head)(reduce(op)(zero)(tail)) } def anyTrue: List[Boolean] => Boolean = reduce(or)(false)
  • 64. def reduce[T, K](op: T => K => K) (zero: K) (list: List[T]): K = list match { case Nil => zero case head :: tail => op(head)(reduce(op)(zero)(tail)) }
  • 65. def reduce[T, K](op: T => K => K) (zero: K) (list: List[T]): K = list match { case Nil => zero case head :: tail => op(head)(reduce(op)(zero)(tail)) } def allTrue: List[Boolean] => Boolean =
  • 66. def reduce[T, K](op: T => K => K) (zero: K) (list: List[T]): K = list match { case Nil => zero case head :: tail => op(head)(reduce(op)(zero)(tail)) } def allTrue: List[Boolean] => Boolean = reduce(and)(true)
  • 67. def reduce[T, K](op: T => K => K) (zero: K) (list: List[T]): K = list match { case Nil => zero case head :: tail => op(head)(reduce(op)(zero)(tail)) }
  • 68. def reduce[T, K](op: T => K => K) (zero: K) (list: List[T]): K = list match { case Nil => zero case head :: tail => op(head)(reduce(op)(zero)(tail)) } A :: B :: C :: D :: E :: Nil
  • 69. def reduce[T, K](op: T => K => K) (zero: K) (list: List[T]): K = list match { case Nil => zero case head :: tail => op(head)(reduce(op)(zero)(tail)) } A :: B :: C :: D :: E :: Nil A :: B :: C :: D :: E :: Nil
  • 70. def reduce[T, K](op: T => K => K) (zero: K) (list: List[T]): K = list match { case Nil => zero case head :: tail => op(head)(reduce(op)(zero)(tail)) } A :: B :: C :: D :: E :: Nil A :: B :: C :: D :: E :: zero
  • 71. def reduce[T, K](op: T => K => K) (zero: K) (list: List[T]): K = list match { case Nil => zero case head :: tail => op(head)(reduce(op)(zero)(tail)) } A :: B :: C :: D :: E :: Nil A op B op C op D op E op zero
  • 72. def <++>[T](a: List[T])(b: List[T]): List[T] =
  • 73. def <++>[T](a: List[T])(b: List[T]): List[T] = reduce(::[T])(b)(a)
  • 74. def lgth[T]: List[T] => Int = reduce(<+>)(0)
  • 75. def <+>[T](el: T)(len: Int) = 1 + len def lgth[T]: List[T] => Int = reduce(<+>)(0)
  • 77. def double(x: Int) = 2 * x def doubleAll: List[Int] => List[Int] =
  • 78. def double(x: Int) = 2 * x def doubleAll: List[Int] => List[Int] = reduce(<+>)(List.empty[Int])
  • 79. def double(x: Int) = 2 * x def <+>(el: Int)(list: List[Int]) = double(el) :: list def doubleAll: List[Int] => List[Int] = reduce(<+>)(List.empty[Int])
  • 80. def double(x: Int) = 2 * x def <+>(el: Int)(list: List[Int]) = double(el) :: list def doubleAll: List[Int] => List[Int] = reduce(<+>)(List.empty[Int])
  • 81. def map[T](f: T => T) =
  • 82. def map[T](f: T => T) = { reduce(<+>)(List.empty[T])(_) }
  • 83. def map[T](f: T => T) = { def <+>(el: T)(list: List[T]) = f(el) :: list reduce(<+>)(List.empty[T])(_) }
  • 84. def map[T](f: T => T) = { def <+>(el: T)(list: List[T]) = f(el) :: list reduce(<+>)(List.empty[T])(_) } def doubleAll: List[Int] => List[Int] = map(double)
  • 85. def map[T](f: T => T) = { def <+>(el: T)(list: List[T]) = f(el) :: list reduce(<+>)(List.empty[T])(_) } def double(x: Int) = 2 * x def doubleAll: List[Int] => List[Int] = map(double)
  • 86. case class TreeOf[T](label: T, subtrees: List[TreeOf[T]])
  • 87. case class TreeOf[T](label: T, subtrees: List[TreeOf[T]]) def redtree[T, K,M] = tree match { case TreeOf(label, subtrees) => }
  • 88. case class TreeOf[T](label: T, subtrees: List[TreeOf[T]]) def redtree[T, K,M](op1: T => K => M) = tree match { case TreeOf(label, subtrees) => op1(label)() }
  • 89. case class TreeOf[T](label: T, subtrees: List[TreeOf[T]]) def redtree[T, K,M](op1: T => K => M) = tree match { case TreeOf(label, subtrees) => op1(label)(_redtree[T, K,M](op1)(op2)(zero)(subtrees)) }
  • 90. def redtree[T, K,M](op1: T => K => M)(op2: M => K => K)(zero: K)(tree: TreeOf[T]): M = tree match { case TreeOf(label, subtrees) => op1(label)(_redtree[T, K,M](op1)(op2)(zero)(subtrees)) }
  • 91. def _redtree[T, K, M](op1: T => K => M)(op2: M => K => K)(zero: K) (subtrees: List[TreeOf[T]]): K = def redtree[T, K,M](op1: T => K => M)(op2: M => K => K)(zero: K)(tree: TreeOf[T]): M = tree match { case TreeOf(label, subtrees) => op1(label)(_redtree[T, K,M](op1)(op2)(zero)(subtrees)) }
  • 92. def _redtree[T, K, M](op1: T => K => M)(op2: M => K => K)(zero: K) (subtrees: List[TreeOf[T]]): K = subtrees match { case Nil => zero def redtree[T, K,M](op1: T => K => M)(op2: M => K => K)(zero: K)(tree: TreeOf[T]): M = tree match { case TreeOf(label, subtrees) => op1(label)(_redtree[T, K,M](op1)(op2)(zero)(subtrees)) }
  • 93. def _redtree[T, K, M](op1: T => K => M)(op2: M => K => K)(zero: K) (subtrees: List[TreeOf[T]]): K = subtrees match { case Nil => zero case subtree :: rest => } def redtree[T, K,M](op1: T => K => M)(op2: M => K => K)(zero: K)(tree: TreeOf[T]): M = tree match { case TreeOf(label, subtrees) => op1(label)(_redtree[T, K,M](op1)(op2)(zero)(subtrees)) }
  • 94. def _redtree[T, K, M](op1: T => K => M)(op2: M => K => K)(zero: K) (subtrees: List[TreeOf[T]]): K = subtrees match { case Nil => zero case subtree :: rest => redtree(op1)(op2)(zero)(subtree)) } def redtree[T, K,M](op1: T => K => M)(op2: M => K => K)(zero: K)(tree: TreeOf[T]): M = tree match { case TreeOf(label, subtrees) => op1(label)(_redtree[T, K,M](op1)(op2)(zero)(subtrees)) }
  • 95. def _redtree[T, K, M](op1: T => K => M)(op2: M => K => K)(zero: K) (subtrees: List[TreeOf[T]]): K = subtrees match { case Nil => zero case subtree :: rest => redtree(op1)(op2)(zero)(subtree)) (_redtree(op1)(op2)(zero)(rest)) } def redtree[T, K,M](op1: T => K => M)(op2: M => K => K)(zero: K)(tree: TreeOf[T]): M = tree match { case TreeOf(label, subtrees) => op1(label)(_redtree[T, K,M](op1)(op2)(zero)(subtrees)) }
  • 96. def _redtree[T, K, M](op1: T => K => M)(op2: M => K => K)(zero: K) (subtrees: List[TreeOf[T]]): K = subtrees match { case Nil => zero case subtree :: rest => op2(redtree(op1)(op2)(zero)(subtree)) (_redtree(op1)(op2)(zero)(rest)) } def redtree[T, K,M](op1: T => K => M)(op2: M => K => K)(zero: K)(tree: TreeOf[T]): M = tree match { case TreeOf(label, subtrees) => op1(label)(_redtree[T, K,M](op1)(op2)(zero)(subtrees)) }
  • 97. def sumtree = redtree(plus)(plus)(0)(_)
  • 98. def labels[T]: TreeOf[T] => List[T] = redtree(::[T])(<++>[T])(List.empty[T])
  • 99. def mapTree[T, K](f: T => K): TreeOf[T] => TreeOf[K] = { def <+>(label: T)(subtrees: List[TreeOf[K]]) = TreeOf(f(label), subtrees) redtree(<+>)(::[TreeOf[K]])(List.empty[TreeOf[K]]) }
  • 100. “Why Functional Programming Matters” J. Hughes http://comjnl.oxfordjournals.org/content/32/2/98. full.pdf
  • 101. “Why Functional Programming Matters” J. Hughes, Nov. 1988 http://comjnl.oxfordjournals.org/content/32/2/98. full.pdf
  • 102. Soft introduction “Why Functional Programming Matters” J. Hughes, Nov. 1988 http://comjnl.oxfordjournals.org/content/32/2/98. full.pdf
  • 103. “Program Design by Calculation” J.N. Oliveira http://www4.di.uminho.pt/~jno/ps/pdbc_part.pdf
  • 104. “Program Design by Calculation” J.N. Oliveira, Draft http://www4.di.uminho.pt/~jno/ps/pdbc_part.pdf
  • 105. Patterns in FP World “Program Design by Calculation” J.N. Oliveira, Draft http://www4.di.uminho.pt/~jno/ps/pdbc_part.pdf
  • 106. Patterns in FP World Math Matters “Program Design by Calculation” J.N. Oliveira, Draft http://www4.di.uminho.pt/~jno/ps/pdbc_part.pdf
  • 107. “A lengthy approach to Haskell fundamentals” http://www.davesquared.net/2012/05/lengthy- approach-to-haskell.html
  • 110.
  • 111.
  • 112.
  • 114. How to do something useful?
  • 115. How to do something useful?
  • 118.
  • 119.
  • 120.
  • 124. “I'm ready to write my first program. How can I print some stuff to console?”
  • 126.
  • 127.
  • 128. “What the *** is a monad?”
  • 130.
  • 131.
  • 132.
  • 134. 1. We understand code 2. It is easier to talk about related ideas and then abstract to something more general
  • 135. 1. We understand code 2. It is easier to talk about related ideas and then abstract to something more general 3. Having abstract model created, we tend to create metaphors
  • 136. 1. We understand code 2. It is easier to talk about related ideas and then abstract to something more general 3. Having abstract model created, we tend to create metaphors 4. Having all above we can formalize
  • 137. Code & related ideas
  • 138.
  • 139. Android, iOS, Windows Phone Partner Lookup
  • 140. case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace)
  • 141. case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace) case class WorkPlace(name: String, street: String)
  • 142. case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace) case class WorkPlace(name: String, street: String) def partnerLookup(geek: Geek): String =
  • 143. case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace) case class WorkPlace(name: String, street: String) def partnerLookup(geek: Geek): String = geek.partner.workPlace.street
  • 144. case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace) case class WorkPlace(name: String, street: String) def partnerLookup(geek: Geek): String = geek.partner.workPlace.street val cheeseCakeFactory = WorkPlace("Cheese Cake Factory", "Cake Street 1") val university = WorkPlace("University", "Academic St. 10")
  • 145. case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace) case class WorkPlace(name: String, street: String) def partnerLookup(geek: Geek): String = geek.partner.workPlace.street val cheeseCakeFactory = WorkPlace("Cheese Cake Factory", "Cake Street 1") val university = WorkPlace("University", "Academic St. 10") var penny = Geek("Penny", workPlace = cheeseCakeFactory) var leonard = Geek("Leonard", workPlace = university)
  • 146. case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace) case class WorkPlace(name: String, street: String) def partnerLookup(geek: Geek): String = geek.partner.workPlace.street val cheeseCakeFactory = WorkPlace("Cheese Cake Factory", "Cake Street 1") val university = WorkPlace("University", "Academic St. 10") var penny = Geek("Penny", workPlace = cheeseCakeFactory) var leonard = Geek("Leonard", workPlace = university) penny = penny.copy(partner = leonard) leonard = leonard.copy(partner = penny)
  • 147. case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace) case class WorkPlace(name: String, street: String) def partnerLookup(geek: Geek): String = geek.partner.workPlace.street val cheeseCakeFactory = WorkPlace("Cheese Cake Factory", "Cake Street 1") val university = WorkPlace("University", "Academic St. 10") var penny = Geek("Penny", workPlace = cheeseCakeFactory) var leonard = Geek("Leonard", workPlace = university) penny = penny.copy(partner = leonard) leonard = leonard.copy(partner = penny) println(partnerLookup(leonard)) println(partnerLookup(penny))
  • 148. case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace) case class WorkPlace(name: String, street: String) def partnerLookup(geek: Geek): String = geek.partner.workPlace.street val cheeseCakeFactory = WorkPlace("Cheese Cake Factory", "Cake Street 1") val university = WorkPlace("University", "Academic St. 10") var penny = Geek("Penny", workPlace = cheeseCakeFactory) var leonard = Geek("Leonard", workPlace = university) penny = penny.copy(partner = leonard) leonard = leonard.copy(partner = penny) println(partnerLookup(leonard)) // output: "Cake Street 1" println(partnerLookup(penny))
  • 149. case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace) case class WorkPlace(name: String, street: String) def partnerLookup(geek: Geek): String = geek.partner.workPlace.street val cheeseCakeFactory = WorkPlace("Cheese Cake Factory", "Cake Street 1") val university = WorkPlace("University", "Academic St. 10") var penny = Geek("Penny", workPlace = cheeseCakeFactory) var leonard = Geek("Leonard", workPlace = university) penny = penny.copy(partner = leonard) leonard = leonard.copy(partner = penny) println(partnerLookup(leonard)) // output: "Cake Street 1" println(partnerLookup(penny)) // output: "Academic St. 10"
  • 150.
  • 151.
  • 152.
  • 154. case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace) case class WorkPlace(name: String, street: String) def partnerLookup(geek: Geek): String = geek.partner.workPlace.street
  • 155.
  • 156. case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace) case class WorkPlace(name: String, street: String) def partnerLookup(geek: Geek): String = geek.partner.workPlace.street
  • 157. case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace) case class WorkPlace(name: String, street: String) def partnerLookup(geek: Geek): String = geek.partner.workPlace.street val university = WorkPlace("University", "Academic St. 10")
  • 158. case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace) case class WorkPlace(name: String, street: String) def partnerLookup(geek: Geek): String = geek.partner.workPlace.street val university = WorkPlace("University", "Academic St. 10") val rajesh = Geek("Rajesh", workPlace = university)
  • 159. case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace) case class WorkPlace(name: String, street: String) def partnerLookup(geek: Geek): String = geek.partner.workPlace.street val university = WorkPlace("University", "Academic St. 10") val rajesh = Geek("Rajesh", workPlace = university) println(partnerLookup(rajesh))
  • 160. case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace) case class WorkPlace(name: String, street: String) def partnerLookup(geek: Geek): String = geek.partner.workPlace.street val university = WorkPlace("University", "Academic St. 10") val rajesh = Geek("Rajesh", workPlace = university) println(partnerLookup(rajesh)) // java.lang.NullPointerException // at wtf.examples.A1$.partnerLookUp(A1.scala:14) // at ...
  • 161. case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace) case class WorkPlace(name: String, street: String) def partnerLookup(geek: Geek): String = geek.partner.workPlace.street val university = WorkPlace("University", "Academic St. 10") val rajesh = Geek("Rajesh", workPlace = university) println(partnerLookup(rajesh)) // java.lang.NullPointerException // at wtf.examples.A1$.partnerLookUp(A1.scala:14) // at ...
  • 163. def partnerLookup(geek: Geek): String = { if(geek != null) { }
  • 164. def partnerLookup(geek: Geek): String = { if(geek != null) { if(geek.partner != null) { }
  • 165. def partnerLookup(geek: Geek): String = { if(geek != null) { if(geek.partner != null) { if(geek.partner.workPlace != null) { }
  • 166. def partnerLookup(geek: Geek): String = { if(geek != null) { if(geek.partner != null) { if(geek.partner.workPlace != null) { if(geek.partner.workPlace.street != null) { }
  • 167. def partnerLookup(geek: Geek): String = { if(geek != null) { if(geek.partner != null) { if(geek.partner.workPlace != null) { if(geek.partner.workPlace.street != null) { return geek.partner.workPlace.street } } } } }
  • 168. def partnerLookup(geek: Geek): String = { if(geek != null) { if(geek.partner != null) { if(geek.partner.workPlace != null) { if(geek.partner.workPlace.street != null) { return geek.partner.workPlace.street } } } } "not found" }
  • 169. val cheeseCakeFactory = WorkPlace("Cheese Cake Factory", "Cake Street 1") val university = WorkPlace("University", "Academic St. 10") var penny = Geek("Penny", workPlace = cheeseCakeFactory) var leonard = Geek("Leonard", workPlace = university) val rajesh = Geek("Rajesh", workPlace = university) penny = penny.copy(partner = leonard) leonard = leonard.copy(partner = penny) println(partnerLookup(leonard)) // output: "Cake Street 1" println(partnerLookup(penny)) // output: "Academic St. 10" println(partnerLookup(rajesh)) // output: "not found"
  • 170. def partnerLookup(geek: Geek): String = { if(geek != null) { if(geek.partner != null) { if(geek.partner.workPlace != null) { if(geek.partner.workPlace.street != null) { return geek.partner.workPlace.street } } } } "not found" }
  • 173. sealed trait Maybe[+A] case class Some[+A](value: A) extends Maybe[A]
  • 174. sealed trait Maybe[+A] case class Some[+A](value: A) extends Maybe[A] case object None extends Maybe[Nothing]
  • 175. sealed trait Maybe[+A] case class Some[+A](value: A) extends Maybe[A] case object None extends Maybe[Nothing] object Maybe { def apply[A](value: A) = Some(value) }
  • 176. sealed trait Maybe[+A] { } object Maybe { def apply[A](value: A) = Some(value) } case class Some[+A](value: A) extends Maybe[A] { } case object None extends Maybe[Nothing] { }
  • 177. sealed trait Maybe[+A] { def andThen[B](f: A => Maybe[B]): Maybe[B] } object Maybe { def apply[A](value: A) = Some(value) } case class Some[+A](value: A) extends Maybe[A] { } case object None extends Maybe[Nothing] { }
  • 178. sealed trait Maybe[+A] { def andThen[B](f: A => Maybe[B]): Maybe[B] } object Maybe { def apply[A](value: A) = Some(value) } case class Some[+A](value: A) extends Maybe[A] { def andThen[B](f: A => Maybe[B]): Maybe[B] = f(value) } case object None extends Maybe[Nothing] { }
  • 179. sealed trait Maybe[+A] { def andThen[B](f: A => Maybe[B]): Maybe[B] } object Maybe { def apply[A](value: A) = Some(value) } case class Some[+A](value: A) extends Maybe[A] { def andThen[B](f: A => Maybe[B]): Maybe[B] = f(value) } case object None extends Maybe[Nothing] { def andThen[B](f: Nothing => Maybe[B]): Maybe[B] = this }
  • 180. case class Geek(name: String, partner: Maybe[Geek] = wtf.monads.None, workPlace: Maybe[WorkPlace]) case class WorkPlace(name: String, street: Maybe[String]) def partnerLookup(geek: Geek): String =
  • 181. case class Geek(name: String, partner: Maybe[Geek] = wtf.monads.None, workPlace: Maybe[WorkPlace]) case class WorkPlace(name: String, street: Maybe[String]) def partnerLookup(geek: Geek): String = geek.partner
  • 182. case class Geek(name: String, partner: Maybe[Geek] = wtf.monads.None, workPlace: Maybe[WorkPlace]) case class WorkPlace(name: String, street: Maybe[String]) def partnerLookup(geek: Geek): String = geek.partner.andThen(p => p.workPlace)
  • 183. case class Geek(name: String, partner: Maybe[Geek] = wtf.monads.None, workPlace: Maybe[WorkPlace]) case class WorkPlace(name: String, street: Maybe[String]) def partnerLookup(geek: Geek): String = geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street)
  • 184. case class Geek(name: String, partner: Maybe[Geek] = wtf.monads.None, workPlace: Maybe[WorkPlace]) case class WorkPlace(name: String, street: Maybe[String]) def partnerLookup(geek: Geek): String = geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match { case wtf.monads.Some(street) => street }
  • 185. case class Geek(name: String, partner: Maybe[Geek] = wtf.monads.None, workPlace: Maybe[WorkPlace]) case class WorkPlace(name: String, street: Maybe[String]) def partnerLookup(geek: Geek): String = geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match { case wtf.monads.Some(street) => street case wtf.monads.None => "not found" }
  • 186. val cheeseCakeFactory = WorkPlace("Cheese Cake Factory", "Cake Street 1") val university = WorkPlace("University", "Academic St. 10") var penny = Geek("Penny", workPlace = cheeseCakeFactory) var leonard = Geek("Leonard", workPlace = university) val rajesh = Geek("Rajesh", workPlace = university) penny = penny.copy(partner = leonard) leonard = leonard.copy(partner = penny) println(partnerLookup(leonard)) println(partnerLookup(penny)) println(partnerLookup(rajesh))
  • 187. val cheeseCakeFactory = WorkPlace("Cheese Cake Factory", "Cake Street 1") val university = WorkPlace("University", "Academic St. 10") var penny = Geek("Penny", workPlace = cheeseCakeFactory) var leonard = Geek("Leonard", workPlace = university) val rajesh = Geek("Rajesh", workPlace = university) penny = penny.copy(partner = leonard) leonard = leonard.copy(partner = penny) println(partnerLookup(leonard)) // output: "Cake Street 1" println(partnerLookup(penny)) // output: "Academic St. 10" println(partnerLookup(rajesh)) // output: "not found"
  • 188. def partnerLookup(geek: Geek): String = geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match { case wtf.monads.Some(street) => street case wtf.monads.None => "not found" }
  • 189. def partnerLookup(geek: Geek): String = geek.partner.workPlace.street def partnerLookup(geek: Geek): String = geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match { case wtf.monads.Some(street) => street case wtf.monads.None => "not found" }
  • 190. def partnerLookup(geek: Geek): String = geek.partner.workPlace.street def partnerLookup(geek: Geek): String = geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match { case wtf.monads.Some(street) => street case wtf.monads.None => "not found" }
  • 191. def partnerLookup(geek: Geek): String = geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match { case wtf.monads.Some(street) => street case wtf.monads.None => "not found" }
  • 192. def partnerLookup(geek: Geek): String = geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match { case wtf.monads.Some(street) => street case wtf.monads.None => "not found" } def partnerLookup(geek: Geek): String =
  • 193. def partnerLookup(geek: Geek): Maybe[String] = geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match { case wtf.monads.Some(street) => street case wtf.monads.None => "not found" } def partnerLookup(geek: Geek): Maybe[String] =
  • 194. with a little bit of magic For-comprehension
  • 195. def partnerLookup(geek: Geek): Maybe[String] = geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match { case wtf.monads.Some(street) => street case wtf.monads.None => "not found" } def partnerLookup(geek: Geek): Maybe[String] = { }
  • 196. def partnerLookup(geek: Geek): Maybe[String] = geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match { case wtf.monads.Some(street) => street case wtf.monads.None => "not found" } def partnerLookup(geek: Geek): Maybe[String] = { import Magic._ }
  • 197. def partnerLookup(geek: Geek): Maybe[String] = geek.partner.andThen(g => g.workPlace).andThen(wp => wp.street) match { case wtf.monads.Some(street) => street case wtf.monads.None => "not found" } def partnerLookup(geek: Geek): Maybe[String] = { import Magic._ for { p <- geek.partner wp <- p.workPlace s <- wp.street } yield (s) }
  • 198. def partnerLookup(geek: Geek): Maybe[String] = geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match { case wtf.monads.Some(street) => street case wtf.monads.None => "not found" } def partnerLookup(geek: Geek): Maybe[String] = { import Magic._ for { p <- geek.partner wp <- p.workPlace s <- wp.street } yield (s) }
  • 199. def partnerLookup(geek: Geek): Maybe[String] = geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match { case wtf.monads.Some(street) => street case wtf.monads.None => "not found" } def partnerLookup(geek: Geek): Maybe[String] = { import Magic._ for { p <- geek.partner wp <- p.workPlace s <- wp.street } yield (s) }
  • 200. def partnerLookup(geek: Geek): Maybe[String] = geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match { case wtf.monads.Some(street) => street case wtf.monads.None => "not found" } def partnerLookup(geek: Geek): Maybe[String] = { import Magic._ for { p <- geek.partner wp <- p.workPlace s <- wp.street } yield (s) }
  • 201. def partnerLookup(geek: Geek): Maybe[String] = geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match { case wtf.monads.Some(street) => street case wtf.monads.None => "not found" } def partnerLookup(geek: Geek): Maybe[String] = { import Magic._ for { p <- geek.partner wp <- p.workPlace s <- wp.street } yield (s) }
  • 202. def partnerLookup(geek: Geek): Maybe[String] = geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match { case wtf.monads.Some(street) => street case wtf.monads.None => "not found" } def partnerLookup(geek: Geek): Maybe[String] = { import Magic._ for { p <- geek.partner wp <- p.workPlace s <- wp.street } yield (s) }
  • 203. def partnerLookup(geek: Geek): Maybe[String] = geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match { case wtf.monads.Some(street) => street case wtf.monads.None => "not found" } def partnerLookup(geek: Geek): Maybe[String] = { import Magic._ for { p <- geek.partner wp <- p.workPlace s <- wp.street } yield (s) }
  • 204. var leonard = Geek("Leonard", workPlace = university, partner = penny) partnerLookup(leonard) // Some("Cake Street 1") // how to print it?
  • 205. sealed trait Maybe[+A] { def andThen[B](f: A => Maybe[B]): Maybe[B] } object Maybe { def apply[A](value: A) = Some(value) } case class Some[+A](value: A) extends Maybe[A] { def andThen[B](f: A => Maybe[B]): Maybe[B] = f(value) } case object None extends Maybe[Nothing] { def andThen[B](f: Nothing => Maybe[B]): Maybe[B] = this }
  • 206. sealed trait Maybe[+A] { def andThen[B](f: A => Maybe[B]): Maybe[B] def within[B](f: A => B): Maybe[B] } object Maybe { def apply[A](value: A) = Some(value) } case class Some[+A](value: A) extends Maybe[A] { def andThen[B](f: A => Maybe[B]): Maybe[B] = f(value) } case object None extends Maybe[Nothing] { def andThen[B](f: Nothing => Maybe[B]): Maybe[B] = this }
  • 207. sealed trait Maybe[+A] { def andThen[B](f: A => Maybe[B]): Maybe[B] def within[B](f: A => B): Maybe[B] } object Maybe { def apply[A](value: A) = Some(value) } case class Some[+A](value: A) extends Maybe[A] { def andThen[B](f: A => Maybe[B]): Maybe[B] = f(value) } case object None extends Maybe[Nothing] { def andThen[B](f: Nothing => Maybe[B]): Maybe[B] = this } Reuse existing functionality like println
  • 208. sealed trait Maybe[+A] { def andThen[B](f: A => Maybe[B]): Maybe[B] def within[B](f: A => B): Maybe[B] } object Maybe { def apply[A](value: A) = Some(value) } case class Some[+A](value: A) extends Maybe[A] { def andThen[B](f: A => Maybe[B]): Maybe[B] = f(value) def within[B](f: A => B): Maybe[B] = Maybe(f(value)) } case object None extends Maybe[Nothing] { def andThen[B](f: Nothing => Maybe[B]): Maybe[B] = this }
  • 209. sealed trait Maybe[+A] { def andThen[B](f: A => Maybe[B]): Maybe[B] def within[B](f: A => B): Maybe[B] } object Maybe { def apply[A](value: A) = Some(value) } case class Some[+A](value: A) extends Maybe[A] { def andThen[B](f: A => Maybe[B]): Maybe[B] = f(value) def within[B](f: A => B): Maybe[B] = Maybe(f(value)) } case object None extends Maybe[Nothing] { def andThen[B](f: Nothing => Maybe[B]): Maybe[B] = this def within[B](f: Nothing => B): Maybe[B] = this }
  • 210. var leonard = Geek("Leonard", workPlace = university, partner = penny) partnerLookup(leonard) // Some("Cake Street 1") // how to print it?
  • 211. var leonard = Geek("Leonard", workPlace = university, partner = penny) partnerLookup(leonard)
  • 212. var leonard = Geek("Leonard", workPlace = university, partner = penny) partnerLookup(leonard).within(println) // output: "Cake Street 1"
  • 213.
  • 214.
  • 215. Android, iOS, Windows Phone Ship Inventory
  • 216. case class Pirate(name: String, ships: Many[Ship]) case class Ship(name: String, hold: Hold) case class Hold(barrel: Many[Barrel]) case class Barrel(amount: Int)
  • 217. case class Pirate(name: String, ships: Many[Ship]) case class Ship(name: String, hold: Hold) case class Hold(barrel: Many[Barrel]) case class Barrel(amount: Int) val jack = Pirate("Captain Jack Sparrow", Many(blackPearl, whitePearl))
  • 218. case class Pirate(name: String, ships: Many[Ship]) case class Ship(name: String, hold: Hold) case class Hold(barrel: Many[Barrel]) case class Barrel(amount: Int) val blackPearl = Ship("Black Pearl", blackHold) val whitePearl = Ship("White Pearl", whiteHold) val jack = Pirate("Captain Jack Sparrow", Many(blackPearl, whitePearl))
  • 219. case class Pirate(name: String, ships: Many[Ship]) case class Ship(name: String, hold: Hold) case class Hold(barrel: Many[Barrel]) case class Barrel(amount: Int) val blackHold = Hold(Empty) val whiteHold = Hold(Many(Barrel(20), Barrel(10))) val blackPearl = Ship("Black Pearl", blackHold) val whitePearl = Ship("White Pearl", whiteHold) val jack = Pirate("Captain Jack Sparrow", Many(blackPearl, whitePearl))
  • 221. sealed trait Many[+A] case class Const[+A](head: A, tail: Many[A]) extends Many[A]
  • 222. sealed trait Many[+A] case class Const[+A](head: A, tail: Many[A]) extends Many[A] case object Empty extends Many[Nothing]
  • 223. sealed trait Many[+A] case class Const[+A](head: A, tail: Many[A]) extends Many[A] case object Empty extends Many[Nothing] object Many { def apply[A](elements: A*): Many[A] = { if(elements.length == 1) Const(elements(0), Empty) else Const(elements(0), apply(elements.drop(1) : _*)) } }
  • 224. sealed trait Many[+A] { def andThen[B](f: A => Many[B]): Many[B] def within[B](f: A => B): Many[B] }
  • 225. case class Const[+A](head: A, tail: Many[A]) extends Many[A] { def andThen[B](f: A => Many[B]): Many[B] = ... def within[B](f: A => B): Many[B] = ... }
  • 226. case class Const[+A](head: A, tail: Many[A]) extends Many[A] { def andThen[B](f: A => Many[B]): Many[B] = ... def within[B](f: A => B): Many[B] = Const(f(head), tail.within(f)) }
  • 227. case class Const[+A](head: A, tail: Many[A]) extends Many[A] { def andThen[B](f: A => Many[B]): Many[B] = concat( f(head), tail.andThen(f) ) def within[B](f: A => B): Many[B] = Const(f(head), tail.within(f)) }
  • 228. case class Const[+A](head: A, tail: Many[A]) extends Many[A] { def andThen[B](f: A => Many[B]): Many[B] = concat( f(head), tail.andThen(f) ) private def concat[A](first: Many[A], second: Many[A]): Many[A] = { … } def within[B](f: A => B): Many[B] = Const(f(head), tail.within(f)) }
  • 229. case class Const[+A](head: A, tail: Many[A]) extends Many[A] { def andThen[B](f: A => Many[B]): Many[B] = concat( f(head), tail.andThen(f) ) private def concat[A](first: Many[A], second: Many[A]): Many[A] = { … } def within[B](f: A => B): Many[B] = Const(f(head), tail.within(f)) } case object Empty extends Many[Nothing] { def andThen[B](f: Nothing => Many[B]): Many[B] = this def within[B](f: Nothing => B): Many[B] = this }
  • 230. val blackPearl = Ship("Black Pearl", Hold(Empty)) val whitePearl = Ship("White Pearl", Hold(Many(Barrel(20), Barrel(10)))) val jack = Pirate("Captain Jack Sparrow", Many(blackPearl, whitePearl))
  • 231. val blackPearl = Ship("Black Pearl", Hold(Empty)) val whitePearl = Ship("White Pearl", Hold(Many(Barrel(20), Barrel(10)))) val jack = Pirate("Captain Jack Sparrow", Many(blackPearl, whitePearl)) jack.ships.andThen(ship => ship.hold.barrel) .within(barrel => barrel.amount)
  • 232. val blackPearl = Ship("Black Pearl", Hold(Empty)) val whitePearl = Ship("White Pearl", Hold(Many(Barrel(20), Barrel(10)))) val jack = Pirate("Captain Jack Sparrow", Many(blackPearl, whitePearl)) jack.ships.andThen(ship => ship.hold.barrel) .within(barrel => barrel.amount) import Magic._ val amounts = for { ship <- jack.ships barrel <- ship.hold.barrel } yield (barrel.amount)
  • 233. val blackPearl = Ship("Black Pearl", Hold(Empty)) val whitePearl = Ship("White Pearl", Hold(Many(Barrel(20), Barrel(10)))) val jack = Pirate("Captain Jack Sparrow", Many(blackPearl, whitePearl)) jack.ships.andThen(ship => ship.hold.barrel) .within(barrel => barrel.amount) import Magic._ val amounts = for { ship <- jack.ships barrel <- ship.hold.barrel } yield (barrel.amount)
  • 234. val blackPearl = Ship("Black Pearl", Hold(Empty)) val whitePearl = Ship("White Pearl", Hold(Many(Barrel(20), Barrel(10)))) val jack = Pirate("Captain Jack Sparrow", Many(blackPearl, whitePearl)) jack.ships.andThen(ship => ship.hold.barrel) .within(barrel => barrel.amount) import Magic._ val amounts = for { ship <- jack.ships barrel <- ship.hold.barrel } yield (barrel.amount)
  • 235. val blackPearl = Ship("Black Pearl", Hold(Empty)) val whitePearl = Ship("White Pearl", Hold(Many(Barrel(20), Barrel(10)))) val jack = Pirate("Captain Jack Sparrow", Many(blackPearl, whitePearl)) jack.ships.andThen(ship => ship.hold.barrel) .within(barrel => barrel.amount) import Magic._ val amounts = for { ship <- jack.ships barrel <- ship.hold.barrel } yield (barrel.amount)
  • 236. val blackPearl = Ship("Black Pearl", Hold(Empty)) val whitePearl = Ship("White Pearl", Hold(Many(Barrel(20), Barrel(10)))) val jack = Pirate("Captain Jack Sparrow", Many(blackPearl, whitePearl)) jack.ships.andThen(ship => ship.hold.barrel) .within(barrel => barrel.amount) import Magic._ val amounts = for { ship <- jack.ships barrel <- ship.hold.barrel } yield (barrel.amount)
  • 237. val blackPearl = Ship("Black Pearl", Hold(Empty)) val whitePearl = Ship("White Pearl", Hold(Many(Barrel(20), Barrel(10)))) val jack = Pirate("Captain Jack Sparrow", Many(blackPearl, whitePearl)) jack.ships.andThen(ship => ship.hold.barrel) .within(barrel => barrel.amount) import Magic._ val amounts = for { ship <- jack.ships barrel <- ship.hold.barrel } yield (barrel.amount)
  • 238. val blackPearl = Ship("Black Pearl", Hold(Empty)) val whitePearl = Ship("White Pearl", Hold(Many(Barrel(20), Barrel(10)))) val jack = Pirate("Captain Jack Sparrow", Many(blackPearl, whitePearl)) jack.ships.andThen(ship => ship.hold.barrel) .within(barrel => barrel.amount) import Magic._ val amounts = for { ship <- jack.ships barrel <- ship.hold.barrel } yield (barrel.amount) println(amounts) // Const(20,Const(10,Empty))
  • 239.
  • 240. ● We've seen two wrapping classes.
  • 241. ● We've seen two wrapping classes. ● All have andThen method.
  • 242. ● We've seen two wrapping classes. ● All have andThen method. ● Each can take a function that can be called on a value wrapped by the structure (which can be empty or there can be multiple instances of it).
  • 243. Both were examples of a Monad!
  • 245. Monad is a container, a box.
  • 246. Monad is a container, a box. A box in which we can store an object. Wrap it.
  • 247. Monad is a container, a box. A box in which we can store an object, a value. A box that has a common interface, which allows us to do one thing: connect sequence of operations on the content of the box.
  • 248. Monad is a container, a box. A box in which we can store an object, a value. A box that has a common interface, which allows us to do one thing: connect sequence of operations on the content of the box. andThen means "do the next things".
  • 249. Monad is a container, a box. A box in which we can store an object, a value. A box that has a common interface, which allows us to do one thing: connect sequence of operations on the content of the box. andThen means "do the next things". But the way it is done depends on a box.
  • 250. Monad is a container, a box. A box in which we can store an object, a value. A box that has a common interface, which allows us to do one thing: connect sequence of operations on the content of the box. andThen means "do the next things". But the way it is done depends on a box.
  • 251. Monad is a container, a box. A box in which we can store an object, a value. A box that has a common interface, which allows us to do one thing: connect sequence of operations on the content of the box. andThen means "do the next things". But the way it is done depends on a box.
  • 252. Monad is a container, a box. A box in which we can store an object, a value. A box that has a common interface, which allows us to do one thing: connect sequence of operations on the content of the box. andThen means "do the next things". But the way it is done depends on a box.
  • 253.
  • 254. Monad is an abstract data type.
  • 255. Monad is an abstract data type. It has two operators: andThen and unit
  • 256. Monad is an abstract data type. It has two operators: andThen and unit object Maybe { def apply[A](value: A) = Some(value) }
  • 257. Monad is an abstract data type. It has two operators: andThen and unit
  • 258. Monad is an abstract data type. It has two operators: andThen and unit
  • 259. Monad is an abstract data type. It has two operators: andThen and unit bind (>>=) // formal name
  • 260. Monad is an abstract data type. It has two operators: andThen and unit bind (>>=) // formal name flatMap // in Scala
  • 261. def partnerLookup(geek: Geek): Maybe[String] = { import Magic._ for { p <- geek.partner wp <- p.workPlace s <- wp.street } yield (s) }
  • 262. def partnerLookup(geek: Geek): Maybe[String] = { import Magic._ for { p <- geek.partner wp <- p.workPlace s <- wp.street } yield (s) }
  • 263. object Magic { case class RichMaybe[A](m: Maybe[A]) { def flatMap[B](f: A => Maybe[B]): Maybe[B] = m.andThen(f) def map[B](f: A => B): Maybe[B] = m.within(f) } implicit def enrich[A](m: Maybe[A]) = RichMaybe(m) }
  • 264. def partnerLookup(geek: Geek): Maybe[String] = { import Magic._ for { p <- geek.partner wp <- p.workPlace s <- wp.street } yield (s) }
  • 265. def partnerLookup(geek: Geek): Maybe[String] = { for { p <- geek.partner wp <- p.workPlace s <- wp.street } yield (s) }
  • 266. Monad is an abstract data type. It has two operators: bind and unit
  • 267. Monad is an abstract data type. It has two operators: bind and unit Those operators must follow certain rules: 1. associativity 2. left identity 3. right identity
  • 268. 1. associativity 2. left identity 3. right identity unit acts approximately as a neutral element of bind
  • 269. 1. associativity 2. left identity 3. right identity unit acts approximately as a neutral element of bind unit allows us to put value into the box without damaging it
  • 270. 1. associativity 2. left identity 3. right identity unit acts approximately as a neutral element of bind unit(x).flatMap(f) == f(x) m.flatMap(unit) == m unit allows us to put value into the box without damaging it
  • 271. 1. associativity 2. left identity 3. right identity “Binding two functions in succession is the same as binding one function that can be determined from them”
  • 272. 1. associativity 2. left identity 3. right identity m.flatMap{f(_)}.flatMap.{g(_)} == m.flatMap{f(_).flatMap.{g(_)}} “Binding two functions in succession is the same as binding one function that can be determined from them”
  • 274. case class User(id: Long, fn: String, ln: String) class Cache { def check(id: Long): Option[User] = ??? } case class UserRepo(cache: Cache) { def retrieve(id: Long): User = ??? } class UserFinder(cache: Cache, repo: UserRepo) { def findUser(id: Long): User = { val maybeUser: Option[User] = cache.check(id) if (maybeUser.isDefined) { maybeUser.get } else { val user: User = repo.retrieve(id) cache.insert(user.id, user) } } }
  • 275. case class User(id: Long, fn: String, ln: String)
  • 276. case class User(id: Long, fn: String, ln: String) class Cache {}
  • 277. case class User(id: Long, fn: String, ln: String) class Cache {} def check(id: Long)(cache: Cache): (Cache, Option[User]) = ...
  • 278. case class User(id: Long, fn: String, ln: String) class Cache {} def check(id: Long)(cache: Cache): (Cache, Option[User]) = ... def retrieve(id: Long)(cache: Cache): (Cache, User) = ...
  • 279. case class User(id: Long, fn: String, ln: String) class Cache {} def check(id: Long)(cache: Cache): (Cache, Option[User]) = ... def retrieve(id: Long)(cache: Cache): (Cache, User) = ... def findUser(id: Long)(cache: Cache): (Cache, User) = { } }
  • 280. case class User(id: Long, fn: String, ln: String) class Cache {} def check(id: Long)(cache: Cache): (Cache, Option[User]) = ... def retrieve(id: Long)(cache: Cache): (Cache, User) = ... def findUser(id: Long)(cache: Cache): (Cache, User) = { val (c, mu) = check(id)(cache) } }
  • 281. case class User(id: Long, fn: String, ln: String) class Cache {} def check(id: Long)(cache: Cache): (Cache, Option[User]) = ... def retrieve(id: Long)(cache: Cache): (Cache, User) = ... def findUser(id: Long)(cache: Cache): (Cache, User) = { val (c, mu) = check(id)(cache) mu match { case Some(u) => (c, u) } } }
  • 282. case class User(id: Long, fn: String, ln: String) class Cache {} def check(id: Long)(cache: Cache): (Cache, Option[User]) = ... def retrieve(id: Long)(cache: Cache): (Cache, User) = ... def findUser(id: Long)(cache: Cache): (Cache, User) = { val (c, mu) = check(id)(cache) mu match { case Some(u) => (c, u) case None => retrieve(id)(c) } } }
  • 283. case class User(id: Long, fn: String, ln: String) class Cache {} def check(id: Long)(cache: Cache): (Cache, Option[User]) = ... def retrieve(id: Long)(cache: Cache): (Cache, User) = ... def findUser(id: Long)(cache: Cache): (Cache, User) = { val (c, mu) = check(id)(cache) mu match { case Some(u) => (c, u) case None => retrieve(id)(c) } } }
  • 284. S => (S, A)
  • 285. State[S, A] S => (S, A)
  • 286. State[S, A] S => (S, A) .run(S)
  • 287. State[S, A] S => (S, A) .map(A => B): State[S, B]
  • 288. State[S, A] S => (S, A) .flatMap(A => State[S, B]): State[S,B]
  • 289. object State { def apply[S, A] (f: S => (S,A)): State[S, A] = }
  • 290. object State { def apply[S, A] (f: S => (S,A)): State[S, A] = new State[S, A] { def run(s: S) = f(s) } }
  • 291. object State { def apply[S, A] (f: S => (S,A)): State[S, A] = new State[S, A] { def run(s: S) = f(s) } } def check(id: String) = State[Cache, Option[User]].apply { }
  • 292. object State { def apply[S, A] (f: S => (S,A)): State[S, A] = new State[S, A] { def run(s: S) = f(s) } } def check(id: String) = State[Cache, Option[User]].apply { (c: Cache) => (c, c.get(id)) }
  • 293. object State { def apply[S, A] (f: S => (S,A)): State[S, A] = new State[S, A] { def run(s: S) = f(s( } } def check(id: String) = State[Cache, Option[User]]{ (c: Cache) => (c, c.get(id)) }
  • 295. trait State[S, +A] { def run(initial: S): (S, A) }
  • 296. trait State[S, +A] { def run(initial: S): (S, A) def map[B](f: A => B): State[S, B] = } }
  • 297. trait State[S, +A] { def run(initial: S): (S, A) def map[B](f: A => B): State[S, B] = State { } } }
  • 298. trait State[S, +A] { def run(initial: S): (S, A) def map[B](f: A => B): State[S, B] = State { s0 => (_, _ ) } } }
  • 299. trait State[S, +A] { def run(initial: S): (S, A) def map[B](f: A => B): State[S, B] = State { s0 => (_, f(a)) } } }
  • 300. trait State[S, +A] { def run(initial: S): (S, A) def map[B](f: A => B): State[S, B] = State { s0 => val (s, a) = run(s0) (_, f(a)) } } }
  • 301. trait State[S, +A] { def run(initial: S): (S, A) def map[B](f: A => B): State[S, B] = State { s0 => val (s, a) = run(s0) (s, f(a)) } } }
  • 302. trait State[S, +A] { def run(initial: S): (S, A) def map[B](f: A => B): State[S, B] = State { s0 => val (s, a) = run(s0) (s, f(a)) } } def flatMap[B](f: A => State[S,B]): State[S, B] = } }
  • 303. trait State[S, +A] { def run(initial: S): (S, A) def map[B](f: A => B): State[S, B] = State { s0 => val (s, a) = run(s0) (s, f(a)) } } def flatMap[B](f: A => State[S,B]): State[S, B] = f(a) } }
  • 304. trait State[S, +A] { def run(initial: S): (S, A) def map[B](f: A => B): State[S, B] = State { s0 => val (s, a) = run(s0) (s, f(a)) } } def flatMap[B](f: A => State[S,B]): State[S, B] = val (s, a) = run(s0) f(a) } }
  • 305. trait State[S, +A] { def run(initial: S): (S, A) def map[B](f: A => B): State[S, B] = State { s0 => val (s, a) = run(s0) (s, f(a)) } } def flatMap[B](f: A => State[S,B]): State[S, B] = State { s0 => val (s, a) = run(s0) f(a) } } }
  • 306. trait State[S, +A] { def run(initial: S): (S, A) def map[B](f: A => B): State[S, B] = State { s0 => val (s, a) = run(s0) (s, f(a)) } } def flatMap[B](f: A => State[S,B]): State[S, B] = State { s0 => val (s, a) = run(s0) f(a).run(s) } } }
  • 307. case class User(id: Long, fn: String, ln: String) class Cache {} def check(id: Long)(cache: Cache): (Cache, Option[User]) = ??? def retrieve(id: Long)(cache: Cache): (Cache, User) = ??? def findUser(id: Long)(cache: Cache): (Cache, User) = { val (c, mu) = check(id)(cache) mu match { case Some(u) => (c, u) case None => retrieve(id)(c) } } }
  • 308. case class User(id: Long, fn: String, ln: String) class Cache {} def check(id: Long)(cache: Cache): (Cache, Option[User]) = ??? def retrieve(id: Long)(cache: Cache): (Cache, User) = ??? def findUser(id: Long)(cache: Cache): (Cache, User) = { val (c, mu) = check(id)(cache) mu match { case Some(u) => (c, u) case None => retrieve(id)(c) } } }
  • 309. case class User(id: Long, fn: String, ln: String) class Cache {} def check(id: Long): State[Cache, Option[User]] = ??? def retrieve(id: Long): State[Cache, User] = ??? def findUser(id: Long): State[Cache, User] = { for { maybeUser <- check(id) user <- maybeUser match { case Some(u) => State { c => (c, u)} case None => retrieve(id) } } yield (user) }
  • 311.
  • 315. Bloggers Conf App ● Can create an account
  • 316. Bloggers Conf App ● Can create an account ● List all bloggers already using the app
  • 317. Bloggers Conf App ● Can create an account ● List all bloggers already using the app ● Mark/unmark other blogger as a friend
  • 318. Bloggers Conf App ● Can create an account ● List all bloggers already using the app ● Mark/unmark other blogger as a friend ● Mark/unmark other blogger as an enemy
  • 319. Bloggers Conf App ● Can create an account ● List all bloggers already using the app ● Mark/unmark other blogger as a friend ● Mark/unmark other blogger as an enemy ● Deactivate its account
  • 320. Bloggers Conf App Bloggers id first_name last_name active 1 Jan Kowalski T 2 Krystian Nowak T 3 Malgorzata Kucharska T
  • 321. Bloggers Conf App Bloggers id first_name last_name active 1 Jan Kowalski T 2 Krystian Nowak T 3 Malgorzata Kucharska T Friends id friend_id 3 1
  • 322. Bloggers Conf App Bloggers id first_name last_name active 1 Jan Kowalski T 2 Krystian Nowak T 3 Malgorzata Kucharska T Friends id friend_id 3 1 Enemies id enemy_id 3 2
  • 323. Structure is not that important
  • 324. Structure is not that important Changes more often than behaviour
  • 325. Start thinking about facts occurring
  • 326. Start thinking about facts occurring Derive structure from them
  • 330. Blogger Account Created (id=3) Befriended Blogger id=1 Made Enemy of Blogger id=2 Bloggers id first_name last_name active 1 Jan Kowalski T 2 Krystian Nowak T Friends id friend_id Enemies id enemy_id
  • 331. Blogger Account Created (id=3) Befriended Blogger id=1 Made Enemy of Blogger id=2 Bloggers id first_name last_name active 1 Jan Kowalski T 2 Krystian Nowak T 3 Malgorzata Kucharska T Friends id friend_id Enemies id enemy_id
  • 332. Blogger Account Created (id=3) Befriended Blogger id=1 Made Enemy of Blogger id=2 Bloggers id first_name last_name active 1 Jan Kowalski T 2 Krystian Nowak T 3 Malgorzata Kucharska T Friends id friend_id 3 1 Enemies id enemy_id
  • 333. Blogger Account Created (id=3) Befriended Blogger id=1 Made Enemy of Blogger id=2 Bloggers id first_name last_name active 1 Jan Kowalski T 2 Krystian Nowak T 3 Malgorzata Kucharska T Friends id friend_id 3 1 Enemies id enemy_id 3 2
  • 337. Event Sourcing The only model that does not lose data
  • 338. “Enemy of my enemy is my friend”
  • 339. Bloggers id first_name last_name active 1 Jan Kowalski T 2 Krystian Nowak T 3 Malgorzata Kucharska T Friends id friend_id 3 1 Enemies id enemy_id 3 2
  • 340. Bloggers id first_name last_name active 1 Jan Kowalski T 2 Krystian Nowak T 3 Malgorzata Kucharska T 4 Tomasz Młynarski T Friends id friend_id 3 1 Enemies id enemy_id 3 2 4 2
  • 341. Bloggers id first_name last_name active 1 Jan Kowalski T 2 Krystian Nowak T 3 Malgorzata Kucharska T 4 Tomasz Młynarski T 5 Monika Jagoda T Friends id friend_id 3 1 Enemies id enemy_id 3 2 4 2 2 5
  • 342. id = 3 id = 2 id = 1 id = 4 id = 5 id = 6
  • 343. id = 3 id = 2 id = 1 id = 4 id = 5 id = 6
  • 344. id = 3 id = 2 id = 1 id = 4 id = 5 id = 6
  • 345. id = 3 id = 2 id = 1 id = 4 id = 5 id = 6
  • 346. id = 3 id = 2 id = 1 id = 4 id = 5 id = 6
  • 347. id = 3 id = 2 id = 1 id = 4 id = 5 id = 6
  • 348. id = 3 id = 2 id = 1 id = 4 id = 5 id = 6
  • 349. id = 3 id = 2 id = 1 id = 4 id = 5 id = 6
  • 350. Benefits of Event Sourcing
  • 351. Benefits of Event Sourcing ● Ability to go in time and figure out exactly what have happened
  • 352. Benefits of Event Sourcing ● Ability to go in time and figure out exactly what have happened ● Scientific measurements over time, compare time periods
  • 353. Benefits of Event Sourcing ● Ability to go in time and figure out exactly what have happened ● Scientific measurements over time, compare time periods ● Built-in audit log
  • 354. Benefits of Event Sourcing ● Ability to go in time and figure out exactly what have happened ● Scientific measurements over time, compare time periods ● Built-in audit log ● Enables temporal querying
  • 355. Benefits of Event Sourcing ● Ability to go in time and figure out exactly what have happened ● Scientific measurements over time, compare time periods ● Built-in audit log ● Enables temporal querying ● Fits well with machine learning
  • 356. Benefits of Event Sourcing ● Ability to go in time and figure out exactly what have happened ● Scientific measurements over time, compare time periods ● Built-in audit log ● Enables temporal querying ● Fits well with machine learning ● Preserves history - question not yet asked
  • 357. Benefits of Event Sourcing ● Ability to go in time and figure out exactly what have happened ● Scientific measurements over time, compare time periods ● Built-in audit log ● Enables temporal querying ● Fits well with machine learning ● Preserves history - question not yet asked ● Writing regression tests is easy
  • 358. Benefits of Event Sourcing ● Ability to go in time and figure out exactly what have happened ● Scientific measurements over time, compare time periods ● Built-in audit log ● Enables temporal querying ● Fits well with machine learning ● Preserves history - question not yet asked ● Writing regression tests is easy ● Polyglot data
  • 359. Drawbacks of Event Sourcing
  • 360. Drawbacks of Event Sourcing ● Historical record of your bad decisions
  • 361. Drawbacks of Event Sourcing ● Historical record of your bad decisions ● Handling event duplicates
  • 362. Drawbacks of Event Sourcing ● Historical record of your bad decisions ● Handling event duplicates ● Data eventually consistent
  • 367. Journal val id = “” val firstName: String = “” val lastName: String = “” val friends: List[String] = List()
  • 368. Journal val id = “” val firstName: String = “” val lastName: String = “” val friends: List[String] = List()
  • 369. Journal val id = “” val firstName: String = “” val lastName: String = “” val friends: List[String] = List()
  • 370. Journal val id = “” val firstName: String = “” val lastName: String = “” val friends: List[String] = List() Initialized(“1”, “Jan”, “Kowalski”
  • 371. Journal val id = “” val firstName: String = “” val lastName: String = “” val friends: List[String] = List() Initialized(“1”, “Jan”, “Kowalski”
  • 372. Journal val id = “1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List()
  • 373. Journal val id = “1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List()
  • 374. Journal val id = “1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List()
  • 375. Journal val id = “1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List() Befriended(“10”)
  • 376. Journal val id = “1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List() Befriended(“10”)
  • 377. Journal val id = “1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List(“10”)
  • 378. Journal val id = “1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”)
  • 379. Journal val id = “1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”)
  • 380. Journal val id = “1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”)
  • 381. Journal val id = “1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List(“10”, “31”)
  • 382. Journal val id = “1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”, “31”)
  • 383. Journal val id = “1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”, “31”) Befriend(“31”)
  • 384. Journal val id = “1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”, “31”) Befriend(“31”)
  • 385. Journal val id = “1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”, “31”) Befriend(“31”) validation
  • 386. Journal val id = “1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”, “31”) Befriend(“31”) validation
  • 387. Journal val id = “1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”, “31”)
  • 388. Journal val id = “1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”, “31”) Befriend(“34”)
  • 389. Journal val id = “1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”, “31”) Befriend(“34”)
  • 390. Journal val id = “1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”, “31”) Befriend(“34”) validation
  • 391. Journal val id = “1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”, “31”) Befriend(“34”) validation
  • 392. Journal val id = “1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”, “31”) Befriend(“34”) Befriended(“34”)
  • 393. Journal val id = “1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”, “31”) Befriend(“34”) Befriended(“34”)
  • 394. Journal val id = “1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”, “31”) Befriend(“34”) Befriended(“34”)
  • 395. Journal val id = “1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”, “31”) Befriend(“34”) Befriended(“34”)
  • 396. Journal val id = “1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”, “31”) Befriend(“34”)
  • 397. Journal val id = “1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”, “31”) Befriend(“34”) ACK
  • 398. Journal val id = “1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List(“10”, “31”, “34”) Befriend(“34”)
  • 399. Journal val id = “1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List(“10”, “31”, “34”)
  • 401. What we are missing?
  • 402. What we are missing? 1. Read-model
  • 403. What we are missing? 1. Read-model 2. Validation
  • 404. And that’s all folks!
  • 409.