Contact
FUNCTIONAL PROGRAMING AND EVENT SOURCING - A PAIR MADE IN HEAVEN - EXTENDED, 2 HOURS LONG BRAINWASH
TL;DR: This is talk is a solid introduction to two (supposedly) different topics: FP & ES. I will cover both the theory and the practice. We will emerage ES+FP application starting from ES+OO one.
While reading blogs or attending conferences, you might have heard about Event Sourcing. But didn't you get this feeling, that while there is a lot of theory out there, it is really hard to see a hands-on example? And even if you find some, those are always orbiting around Object Oriented concepts?
Greg Young once said "When we talk about Event Sourcing, current state is a left-fold of previous behaviours. Nothing new to Functional Programmers". If Functional Programming is such a natural concept for event sourced systems, shouldn't they fit together on a single codebase?
In this talk we will quickly introduce Event Sourcing (but without going into details), we will introduce some functional concepts as well (like State monad). Armoured with that knowledge we will try to transform sample ES application (OO-style, tightly coupled with framework) to frameworkless, FP-style solution).
Talk is targeted for beginner and intermediate audience. Examples will be in Scala but nothing fancy - normal syntax.
This talk is an extended version of a presentation "Event Sourcing & Functional Programming - a pair made in heaven". It is enriched with content of presentations: "Monads - asking the right question" and "It's all been done before - The Hitchhiker's Guide to Time Travel".
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) { ??? }
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
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))
}
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
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"
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 ...
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] =
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))
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))
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).
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.
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”
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
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
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
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”)