Date post: | 22-Jan-2017 |
Category: |
Software |
Upload: | pawel-szulc |
View: | 674 times |
Download: | 0 times |
Functional Programming & Event Sourcing
A pair made in heaven
twitter: @rabbitonweb, email: [email protected]
“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)
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)
}
}
}
/** * 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 reversedpublic List<T> reverse(List<t> list) { ??? }
“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
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)
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)
}
}
}
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) =
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) =
(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]].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] {
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)
}
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
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
The Structure
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
Befriended Blogger id=2
Unfriended 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
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
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● Historical record of your bad decisions● Handling event duplicates● Data eventually consistent
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”)
Paweł Szulchttp://rabbitonweb.com
@rabbitonwebhttps://github.com/rabbitonweb/