SlideShare una empresa de Scribd logo
1 de 78
Descargar para leer sin conexión
QUERY MONAD:QUERY MONAD:
A SPECIALIZED READER FOR PURE DATABASEA SPECIALIZED READER FOR PURE DATABASE
PROGRAMMINGPROGRAMMING
STÉPHANE TANKOUA (GITHUB: @STANKOUA / TWITTER: @STEFSHINE)STÉPHANE TANKOUA (GITHUB: @STANKOUA / TWITTER: @STEFSHINE)
SCALA.IO 2018SCALA.IO 2018
1
SUMMARYSUMMARY
2
SUMMARYSUMMARY
The rationale
2
SUMMARYSUMMARY
The rationale
Design guidelines
2
SUMMARYSUMMARY
The rationale
Design guidelines
Specializing Reader
2
SUMMARYSUMMARY
The rationale
Design guidelines
Specializing Reader
Interpretation
2
SUMMARYSUMMARY
The rationale
Design guidelines
Specializing Reader
Interpretation
Library
2
THE RATIONALETHE RATIONALE
3
CONTEXT: TECH STACKCONTEXT: TECH STACK
Technologies we commonly use:
Play (web)
Anorm (persistence)
Relational database (PostgreSQL)
4
FOCUSING ON ANORMFOCUSING ON ANORM
How to use it (from of cial documentation)
import anorm._
def one(implicit c: java.sql.Connection): Int = {
val query: SimpleSql = SQL("select 1 as res")
query.as(SqlParser.int("res").single)
}
5
OUR AIMOUR AIM
What could be improved ?
6
OUR AIMOUR AIM
What could be improved ?
Not blocking the current thread
6
OUR AIMOUR AIM
What could be improved ?
Not blocking the current thread
Representing effect as type (DB read is a side-effect)
6
OUR AIMOUR AIM
What could be improved ?
Not blocking the current thread
Representing effect as type (DB read is a side-effect)
Doesn't compose
6
OUR AIMOUR AIM
What could be improved ?
Not blocking the current thread
Representing effect as type (DB read is a side-effect)
Doesn't compose
Purity (Referential Transparency + no side-effects)
6
REFERENTIAL TRANSPARENCYREFERENTIAL TRANSPARENCY
7
An expression is called referentially transparent if it
can be replaced with its corresponding value
without changing the program's behavior.
8 . 1
REFERENTIAL TRANSPARENCYREFERENTIAL TRANSPARENCY
EXAMPLEEXAMPLE
is equivalent to:
def inc(x: Int): Int = x + 1
val res = inc(5) + inc(5)
// console:> res: Int = 12
val res = 6 + 6 // Replacing inc(5) by its value
// console:> res: Int = 12
8 . 2
REFERENTIAL TRANSPARENCYREFERENTIAL TRANSPARENCY
BACK TO ANORM EXAMPLEBACK TO ANORM EXAMPLE
is NOT equivalent to:
Because the database will not be called
implicit val c: Connection = ...
def one(implicit c: Connection): Int = {
val query: SimpleQuery = SQL("select 1 as res")
query.as(SqlParser.str("res").single)
}
val res = one
// console:> res: Int = 1
val res = 1 // Replacing one by its value
// console:> res: Int = 1
8 . 3
SUMMARYSUMMARY
The rationale
Design guidelines
Specializing Reader
Interpretation
Library
9
DESIGN GUIDELINESDESIGN GUIDELINES
10
PRINCIPLESPRINCIPLES
Functional principles to rely on:
11 . 1
PRINCIPLESPRINCIPLES
Functional principles to rely on:
separating representation from interpretation
case class Plus(x: Int, y: Int)
def run(op: Plus): Int = op.x + op.y
val op = Plus(1, 2)
// console:> op: Plus = Plus(1,2)
run(op)
// console:> res0: Int = 3
11 . 2
PRINCIPLESPRINCIPLES
Functional principles to rely on:
separating representation from their interpretation
composition
// Composition with For-comprehensions (ie monadic)
val zipped = for {
x <- List(1, 2, 3)
y <- List("a", "b", "c")
} yield (x, y)
// console:> zipped: List[Int] = List((1, "a"), (2, "b"), (3, "c"))
11 . 3
PRINCIPLESPRINCIPLES
Functional principles to rely on:
separating representation from interpretation
composition
lazy evaluation
val stream = 100 #:: 200 #:: 85 #:: Stream.empty
val doubledStream = stream.map(_ * 2) // do nothing
doubledStream.print()
// console:> 200, 400, 170, empty
11 . 4
PRINCIPLESPRINCIPLES
Functional principles to rely on:
separating representation from interpretation
composition
lazy evaluation
11 . 5
THE PURPOSETHE PURPOSE
Solution: Using a lazy representational structure which compose (F)
case class User(id: Int, name: String, age: Int)
def readUser(id: Int): F[User]
def updateUser(user: User): F[Unit]
// Composing DB atomic operations into a single one
def changeUserName(id: Int, newName: String): F[Unit] =
for {
user <- readUser(id)
newUser = user.copy(name = newName)
result <- updateUser(newUser)
} yield result
12 . 1
THE PURPOSETHE PURPOSE
12 . 2
THE PURPOSETHE PURPOSE
Lazy representational structure => interpreter needed
val op = changeUserName(1, "John Doe")
val result: Future[Unit] = interpreter.run(op)
12 . 2
THE PURPOSETHE PURPOSE
Lazy representational structure => interpreter needed
Interpretation process:
val op = changeUserName(1, "John Doe")
val result: Future[Unit] = interpreter.run(op)
12 . 2
THE PURPOSETHE PURPOSE
Lazy representational structure => interpreter needed
Interpretation process:
1. Provide context
val op = changeUserName(1, "John Doe")
val result: Future[Unit] = interpreter.run(op)
12 . 2
THE PURPOSETHE PURPOSE
Lazy representational structure => interpreter needed
Interpretation process:
1. Provide context
2. Execute our operation (within our effect)
val op = changeUserName(1, "John Doe")
val result: Future[Unit] = interpreter.run(op)
12 . 2
THE PURPOSETHE PURPOSE
Lazy representational structure => interpreter needed
Interpretation process:
1. Provide context
2. Execute our operation (within our effect)
3. Translate it into another effect (F -> Future)
val op = changeUserName(1, "John Doe")
val result: Future[Unit] = interpreter.run(op)
12 . 2
SHAPING OUR SOLUTIONSHAPING OUR SOLUTION
import anorm.SimpleSql
def readUser(id: Int)(implicit c: Connection): SimpleSql[User] =
SQL(s"select * from user where id = $id")
def updateUser(user: User)(implicit c: Connection): SimpleSql[Unit] =
SQL(s"update user set ...")
// NO: doesn't compose
// def changeUserName(id: Int, newName: String): SimpleSql[Unit] = for {
// user <- readUser(id)
// newUser = user.copy(name = newUser)
// result <- updateUser(user)
// } yield result
13 . 1
SHAPING OUR SOLUTIONSHAPING OUR SOLUTION
def readUser(id: Int): Future[User] =
Future(SQL(s"select * from user where id = $id").as(...))
def updateUser(user: User): Future[Unit] =
Future(SQL(s"update user set ...").execute())
// commit when ???
def changeUserName(id:Int, newName:String)(implicit c:Connection): Future[Unit] =
for {
user <- readUser(id)
newUser = user.copy(name = newName)
result <- updateUser(newUser)
} yield result
13 . 2
SHAPING OUR SOLUTIONSHAPING OUR SOLUTION
Desiging our lazy representational structure which compose:
import java.sql.Connection
case class Query[A](f: Connection => A) {
def map[B](f: A => B): Query[B]
def flatMap[B](f: A => Query[B]): Query[B]
}
13 . 3
SHAPING OUR SOLUTIONSHAPING OUR SOLUTION
How could we use it ?
def one: Query[Int] = Query { implicit c =>
SQL("Select 1 as one").as(SqlParser.str("one").single)
}
13 . 4
ABOUT THE PRINCIPLESABOUT THE PRINCIPLES
Real word example (create a user's bank account):
case class User(id: String, name: String)
case class Account(id: String, userId: String, balance: BigDecimal)
def createUser(user: User): Query[Unit] = ???
def createAccount(account: Account, userId: String): Query[Unit] = ???
def createUserWithAccount(user: User, account: Account): Query[Unit] =
for { // composing operations
_ <- createUser(user)
_ <- createAccount(account, user.id)
} yield () // transactional
val op: Query[Unit] =
createUserWithAccount(User("1", "John Doe"), Account("a1", "1", 100))
// Query[Unit] => lazy => purity !!!
14 . 1
ABOUT THE PRINCIPLESABOUT THE PRINCIPLES
Real word example (create a user's bank account):
val runner = ???
runner(op) // we run the operations (caution: side-effect)
// Do it at end of your program
// Could not compose anymore
14 . 2
QUERY IMPLEMENTATIONQUERY IMPLEMENTATION
Idea: Could we link it to an existing one ?
15
QUERY IMPLEMENTATIONQUERY IMPLEMENTATION
Idea: Could we link it to an existing one ?
From signature: a wrapper over a function (of 1 arg)
case class Query[A](f: Connection => A)
15
SUMMARYSUMMARY
The rationale
Design guidelines
Specializing Reader
Interpretation
Library
16
SPECIALIZINGSPECIALIZING ReaderReader
17
INTRODUCING READERINTRODUCING READER
Reader: monad which pass along a context
From signature: a wrapper over a function (of 1 arg)
class Reader[-A, +B](f: A => B)
18 . 1
INTRODUCING READERINTRODUCING READER
Reader: monad which pass along a context
From signature: a wrapper over a function (of 1 arg)
map == function composition (g andThen f)
class Reader[-A, +B](f: A => B)
18 . 1
INTRODUCING READERINTRODUCING READER
Reader: monad which pass along a context
From signature: a wrapper over a function (of 1 arg)
map == function composition (g andThen f)
atMap == function composition with wrapping / unwrapping
class Reader[-A, +B](f: A => B)
18 . 1
INTRODUCING READERINTRODUCING READER
Implementation could be easily derived:
case class Reader[-A, +B](f: A => B) {
def map[C](g: B => C): Reader[A, C] =
Reader(g compose f) // function composition
def flatMap[C](g: B => Reader[A, C]): Reader[A, C] =
Reader(a => g(f(a)).f(a)) // composition & unwrapping
}
object Reader {
def pure[C, A](a: A): Reader[C, A] = Reader(_ => a)
// Get only the context
def ask[A]: Reader[A, A] = Reader(identity)
}
18 . 2
DEDUCING IMPLEMENTATIONDEDUCING IMPLEMENTATION
Query could be implemented in term of Reader:
type Query[A] = Reader[Connection, A]
object Query {
def pure[A](a: A) = Reader.pure[Connection, A](a)
def apply[A](f: Connection => A) = new Query(f)
}
19
HANDLING MONAD STACKHANDLING MONAD STACK
Recurrent problem with monad:
Query == Reader
How to flatMap over Reader stack ?
def divideBy2(x: Int): Query[Option[Int]]
def divideBy3(x: Int): Query[Option[Int]]
// Query is monad, Option is a monad
// but Query[Option[_]] is not
// how to compose Query[Option[_]] without nesting ?
def divideBy6(x: Int): Query[Option[Int]] = for {
y <- divideBy2(x)
z <- divideBy3(y) // DOES NOT COMPILE: divideBy3 expect Int, y is Option[Int]
} yield z
20
TRANSFORMERSTRANSFORMERS
One solution: Monad transformer
Transformer for Reader: ReaderT
/** @param F the monad we add to our stack */
case class ReaderT[F[_], -A, +B](f: A => F[B]) {
def map[C](g: B => C)(implicit F: Functor[F]): ReaderT[F, A, C] =
ReaderT(a => F.map(f(a))(g))
def flatMap[C](g: B => ReaderT[F, A, C])(
implicit M: Monad[F]
): ReaderT[F, A, C] =
ReaderT(a => M.flatMap(f(a))(b => g(b).f(a)))
}
21 . 1
TRANSFORMERSTRANSFORMERS
Bonus: Reader in terms of ReaderT
type Id[A] = A
type Reader[C, A] = ReaderT[Id, C, A]
21 . 2
QUERYT TRANSFORMERQUERYT TRANSFORMER
type QueryT[F[_], A] = ReaderT[F, Connection, A]
type Query[A] = QueryT[Id, A]
object QueryT {
def apply[M[_], A](run: Connection => M[A]):QueryT[M, A] = new QueryT(run)
def pure[M[_]: Applicative, A](a: A) = ReaderT.pure[M, Connection, A](a)
def liftF[M[_], A](ma: M[A]) = ReaderT.liftF[M, Connection, A](ma)
def fromQuery[M[_], A](query: Query[M[A]]): QueryT[M, A] =
QueryT[M, A](query.run)
}
22
SUMMARYSUMMARY
The rationale
Design guidelines
Specializing Reader
Interpretation
Library
23
INTERPRETATIONINTERPRETATION
24
GETTING CONTEXTGETTING CONTEXT
25
GETTING CONTEXTGETTING CONTEXT
Pre-requisite: how to get the context ?
25
GETTING CONTEXTGETTING CONTEXT
Pre-requisite: how to get the context ?
Need to abstract the way we get the connection
25
GETTING CONTEXTGETTING CONTEXT
Pre-requisite: how to get the context ?
Need to abstract the way we get the connection
solution: Loan pattern
trait WithResource {
def apply[A](f: Connection => Future[A]): Future[A]
}
25
GETTING CONTEXTGETTING CONTEXT
Pre-requisite: how to get the context ?
Need to abstract the way we get the connection
solution: Loan pattern
User need to implement it and pass it when trying to interpret a
query
trait WithResource {
def apply[A](f: Connection => Future[A]): Future[A]
}
25
RESOURCE LOANER EXAMPLERESOURCE LOANER EXAMPLE
val loaner = new WithResource {
def apply[A](f: Connection => Future[A]): Future[A] = {
val conn: Connection = DriverManager.getConnection(URL, USER, PASSWD)
f(conn)
.andThen {
case Success(x) =>
connection.commit()
x
case Failure(ex) =>
connection.rollback()
}
.andThen { case _ => connection.close() }
}
}
26
NAIVE INTERPRETERNAIVE INTERPRETER
27
NAIVE INTERPRETERNAIVE INTERPRETER
Naive runner implementation
class QueryRunner(wr: WithResource)(implicit ec: ExecutionContext) {
def apply[M[_], T](query: QueryT[M, T]): Future[M[T]] =
wr { connection =>
Future(query.f(connection))
}
}
27
NAIVE INTERPRETERNAIVE INTERPRETER
Naive runner implementation
Edge case: M == Future
class QueryRunner(wr: WithResource)(implicit ec: ExecutionContext) {
def apply[M[_], T](query: QueryT[M, T]): Future[M[T]] =
wr { connection =>
Future(query.f(connection))
}
}
27
NAIVE INTERPRETERNAIVE INTERPRETER
Naive runner implementation
Edge case: M == Future
The connection will be closed before the termination of our
operation
class QueryRunner(wr: WithResource)(implicit ec: ExecutionContext) {
def apply[M[_], T](query: QueryT[M, T]): Future[M[T]] =
wr { connection =>
Future(query.f(connection))
}
}
27
REFINED INTERPRETERREFINED INTERPRETER
How to handle composition with asynchronous effects ?
Solution: A typeclass which handle composition
trait ComposeWithCompletion[F[_], Out] {
type Outer
def apply[In](loaner: WithResource[In], f: In => F[Out]): Future[Outer]
}
28 . 1
REFINED INTERPRETERREFINED INTERPRETER
Two instances of ComposeWithCompletion:
one to compose on Future
implicit def futureOut[A] = // asynchronous effect
new ComposeWithCompletion[Future, A] {
type Outer = A
def apply[In](
loaner: WithResource[In],
f: In => Future[A]
): Future[Outer] = loaner(f)
}
28 . 2
REFINED INTERPRETERREFINED INTERPRETER
Two instances of ComposeWithCompletion:
one for other effects (with low priority)
implicit def pureOut[F[_], A] = // synchronous effects
new ComposeWithCompletion[F, A] {
type Outer = F[A]
def apply[In](loaner: WithResource[In], f: In => F[A]): Future[Outer] =
loaner(r => Future(f(r)))
}
28 . 3
REFINED INTERPRETERREFINED INTERPRETER
Two instances of ComposeWithCompletion:
one to compose on Future
one for other effects (with low priority)
trait LowPriorityCompose {
implicit def pureOut[F[_], A] = ???
}
object ComposeWithCompletion extends LowPriorityCompose {
implicit def futureOut[A] = ???
}
28 . 4
COMPLETECOMPLETE QUERYRUNNERQUERYRUNNER
Updating our QueryRunner:
class QueryRunner(wr: WithResource)(
implicit ec: ExecutionContext
) {
def apply[M[_], T](query: QueryT[M, T])(
implicit cmp: ComposeWithCompletion[M, T]
): Future[cmp.Outer] =
compose(wr, query.run)
}
29
NOTE ON ASYNC EFFECTNOTE ON ASYNC EFFECT
case class User(id: Int, email: String)
def saveUser(user: User): Query[Unit] = ???
def sendNotificationEmail(userEmail: String): Future[Unit] = ???
// DO NOT DO THIS => Not atomic !!!!!!!!
def createAndNotifyUser(user: User): QueryT[Future, Unit] = ???
// for {
// _ <- saveUser(user).map(Future.successful)
// _ <- QueryT(_ => sendNotificationEmail(user.email))
// }
30
RESOURCE ABSTRACTIONRESOURCE ABSTRACTION
Could we abstract query to handle resource other than
java.sql.Connection
type Query[Resource, A] = Reader[Resource, A]
type QueryT[F[_], Resource, A] = ReaderT[F, Resource, A]
trait WithResource[Resource] {
def apply[A](f: Resource => A): A
}
// Query aliases
type SqlQuery[A] = Query[Connection, A]
type SqlQueryT[F[_], A] = QueryT[F, Connection, A]
31
SUMMARYSUMMARY
The rationale
Design guidelines
Specializing Reader
Interpretation
Library
32
LIBRARYLIBRARY
33
QUERY-MONAD LIBRARYQUERY-MONAD LIBRARY
compiled into a library:
open-source (contribution accepted ;-) )
modulable and extensible
based on cats
https://github.com/zengularity/query-
monad
34
FEATURES:FEATURES:
abstracted Query
type Query[Resource, A] = Reader[Resource, A]
type QueryT[F[_], Resource, A] = ReaderT[F, Resource, A]
type QueryO[Resource, A] = QueryT[Option, Resource, A]
type QueryE[Resource, Err, A] = QueryT[Either[Err, ?], Resource, A]
35
FEATURES:FEATURES:
aliases for sql databases (context == java.sql.Connection)
import java.sql.Connection
type SqlQuery[A] = Query[Connection, A]
type SqlQueryT[F[_], A] = QueryT[F, Connection, A]
type SqlQueryO[A] = QueryO[Connection, A]
type SqlQueryE[A, Err] = QueryE[Connection, A, Err]
// aliases for interpreter
type WithSqlConnection = WithResource[Connection]
type SqlQueryRunner = QueryRunner[Connection]
36
FEATURES:FEATURES:
Resource loaner implementation for Play 2
import java.sql.Connection
import play.api.db.Database
class WithPlayTransaction(db: Database) extends WithSqlConnection {
def apply[A](f: Connection => A): A = { ... }
}
37
QUESTIONS ?QUESTIONS ?
(I HOPE NO)(I HOPE NO)
BUT HOPE YOUBUT HOPE YOU ITIT
38

Más contenido relacionado

La actualidad más candente

Coding Guidelines - Crafting Clean Code
Coding Guidelines - Crafting Clean CodeCoding Guidelines - Crafting Clean Code
Coding Guidelines - Crafting Clean CodeGanesh Samarthyam
 
Planet-HTML5-Game-Engine Javascript Performance Enhancement
Planet-HTML5-Game-Engine Javascript Performance EnhancementPlanet-HTML5-Game-Engine Javascript Performance Enhancement
Planet-HTML5-Game-Engine Javascript Performance Enhancementup2soul
 
Being functional in PHP (PHPDay Italy 2016)
Being functional in PHP (PHPDay Italy 2016)Being functional in PHP (PHPDay Italy 2016)
Being functional in PHP (PHPDay Italy 2016)David de Boer
 
One Monad to Rule Them All
One Monad to Rule Them AllOne Monad to Rule Them All
One Monad to Rule Them AllJohn De Goes
 
OOP and FP - Become a Better Programmer
OOP and FP - Become a Better ProgrammerOOP and FP - Become a Better Programmer
OOP and FP - Become a Better ProgrammerMario Fusco
 
Functions in python
Functions in pythonFunctions in python
Functions in pythonIlian Iliev
 
Category theory, Monads, and Duality in the world of (BIG) Data
Category theory, Monads, and Duality in the world of (BIG) DataCategory theory, Monads, and Duality in the world of (BIG) Data
Category theory, Monads, and Duality in the world of (BIG) Datagreenwop
 
Monad Transformers In The Wild
Monad Transformers In The WildMonad Transformers In The Wild
Monad Transformers In The WildStackMob Inc
 
Creating Domain Specific Languages in Python
Creating Domain Specific Languages in PythonCreating Domain Specific Languages in Python
Creating Domain Specific Languages in PythonSiddhi
 
Tuga IT 2017 - What's new in C# 7
Tuga IT 2017 - What's new in C# 7Tuga IT 2017 - What's new in C# 7
Tuga IT 2017 - What's new in C# 7Paulo Morgado
 
Implementing the IO Monad in Scala
Implementing the IO Monad in ScalaImplementing the IO Monad in Scala
Implementing the IO Monad in ScalaHermann Hueck
 
Introduction to julia
Introduction to juliaIntroduction to julia
Introduction to julia岳華 杜
 
Apache Commons - Don\'t re-invent the wheel
Apache Commons - Don\'t re-invent the wheelApache Commons - Don\'t re-invent the wheel
Apache Commons - Don\'t re-invent the wheeltcurdt
 
Python Functions (PyAtl Beginners Night)
Python Functions (PyAtl Beginners Night)Python Functions (PyAtl Beginners Night)
Python Functions (PyAtl Beginners Night)Rick Copeland
 

La actualidad más candente (20)

Javascript
JavascriptJavascript
Javascript
 
Coding Guidelines - Crafting Clean Code
Coding Guidelines - Crafting Clean CodeCoding Guidelines - Crafting Clean Code
Coding Guidelines - Crafting Clean Code
 
Planet-HTML5-Game-Engine Javascript Performance Enhancement
Planet-HTML5-Game-Engine Javascript Performance EnhancementPlanet-HTML5-Game-Engine Javascript Performance Enhancement
Planet-HTML5-Game-Engine Javascript Performance Enhancement
 
C# 7
C# 7C# 7
C# 7
 
Being functional in PHP (PHPDay Italy 2016)
Being functional in PHP (PHPDay Italy 2016)Being functional in PHP (PHPDay Italy 2016)
Being functional in PHP (PHPDay Italy 2016)
 
One Monad to Rule Them All
One Monad to Rule Them AllOne Monad to Rule Them All
One Monad to Rule Them All
 
What's New In C# 7
What's New In C# 7What's New In C# 7
What's New In C# 7
 
Java 8 Workshop
Java 8 WorkshopJava 8 Workshop
Java 8 Workshop
 
OOP and FP - Become a Better Programmer
OOP and FP - Become a Better ProgrammerOOP and FP - Become a Better Programmer
OOP and FP - Become a Better Programmer
 
Monadic Java
Monadic JavaMonadic Java
Monadic Java
 
Functions in python
Functions in pythonFunctions in python
Functions in python
 
Category theory, Monads, and Duality in the world of (BIG) Data
Category theory, Monads, and Duality in the world of (BIG) DataCategory theory, Monads, and Duality in the world of (BIG) Data
Category theory, Monads, and Duality in the world of (BIG) Data
 
Monad Transformers In The Wild
Monad Transformers In The WildMonad Transformers In The Wild
Monad Transformers In The Wild
 
Creating Domain Specific Languages in Python
Creating Domain Specific Languages in PythonCreating Domain Specific Languages in Python
Creating Domain Specific Languages in Python
 
Tuga IT 2017 - What's new in C# 7
Tuga IT 2017 - What's new in C# 7Tuga IT 2017 - What's new in C# 7
Tuga IT 2017 - What's new in C# 7
 
Implementing the IO Monad in Scala
Implementing the IO Monad in ScalaImplementing the IO Monad in Scala
Implementing the IO Monad in Scala
 
Introduction to julia
Introduction to juliaIntroduction to julia
Introduction to julia
 
Apache Commons - Don\'t re-invent the wheel
Apache Commons - Don\'t re-invent the wheelApache Commons - Don\'t re-invent the wheel
Apache Commons - Don\'t re-invent the wheel
 
Python Functions (PyAtl Beginners Night)
Python Functions (PyAtl Beginners Night)Python Functions (PyAtl Beginners Night)
Python Functions (PyAtl Beginners Night)
 
Sneaking inside Kotlin features
Sneaking inside Kotlin featuresSneaking inside Kotlin features
Sneaking inside Kotlin features
 

Similar a Talk - Query monad

Introduction to Functional Programming with Scala
Introduction to Functional Programming with ScalaIntroduction to Functional Programming with Scala
Introduction to Functional Programming with Scalapramode_ce
 
ZIO: Powerful and Principled Functional Programming in Scala
ZIO: Powerful and Principled Functional Programming in ScalaZIO: Powerful and Principled Functional Programming in Scala
ZIO: Powerful and Principled Functional Programming in ScalaWiem Zine Elabidine
 
Pydiomatic
PydiomaticPydiomatic
Pydiomaticrik0
 
From Java to Scala - advantages and possible risks
From Java to Scala - advantages and possible risksFrom Java to Scala - advantages and possible risks
From Java to Scala - advantages and possible risksSeniorDevOnly
 
Things about Functional JavaScript
Things about Functional JavaScriptThings about Functional JavaScript
Things about Functional JavaScriptChengHui Weng
 
PyData NYC 2019
PyData NYC 2019PyData NYC 2019
PyData NYC 2019Li Jin
 
JDK1.7 features
JDK1.7 featuresJDK1.7 features
JDK1.7 featuresindia_mani
 
Столпы функционального программирования для адептов ООП, Николай Мозговой
Столпы функционального программирования для адептов ООП, Николай МозговойСтолпы функционального программирования для адептов ООП, Николай Мозговой
Столпы функционального программирования для адептов ООП, Николай МозговойSigma Software
 
Scalapeno18 - Thinking Less with Scala
Scalapeno18 - Thinking Less with ScalaScalapeno18 - Thinking Less with Scala
Scalapeno18 - Thinking Less with ScalaDaniel Sebban
 
Scala is java8.next()
Scala is java8.next()Scala is java8.next()
Scala is java8.next()daewon jeong
 
Functional Programming You Already Know
Functional Programming You Already KnowFunctional Programming You Already Know
Functional Programming You Already KnowKevlin Henney
 

Similar a Talk - Query monad (20)

Introduction to Functional Programming with Scala
Introduction to Functional Programming with ScalaIntroduction to Functional Programming with Scala
Introduction to Functional Programming with Scala
 
ZIO: Powerful and Principled Functional Programming in Scala
ZIO: Powerful and Principled Functional Programming in ScalaZIO: Powerful and Principled Functional Programming in Scala
ZIO: Powerful and Principled Functional Programming in Scala
 
Functional programming
Functional programmingFunctional programming
Functional programming
 
Pydiomatic
PydiomaticPydiomatic
Pydiomatic
 
Python idiomatico
Python idiomaticoPython idiomatico
Python idiomatico
 
From Java to Scala - advantages and possible risks
From Java to Scala - advantages and possible risksFrom Java to Scala - advantages and possible risks
From Java to Scala - advantages and possible risks
 
Coding in Style
Coding in StyleCoding in Style
Coding in Style
 
Things about Functional JavaScript
Things about Functional JavaScriptThings about Functional JavaScript
Things about Functional JavaScript
 
PyData NYC 2019
PyData NYC 2019PyData NYC 2019
PyData NYC 2019
 
JDK1.7 features
JDK1.7 featuresJDK1.7 features
JDK1.7 features
 
Столпы функционального программирования для адептов ООП, Николай Мозговой
Столпы функционального программирования для адептов ООП, Николай МозговойСтолпы функционального программирования для адептов ООП, Николай Мозговой
Столпы функционального программирования для адептов ООП, Николай Мозговой
 
Scalapeno18 - Thinking Less with Scala
Scalapeno18 - Thinking Less with ScalaScalapeno18 - Thinking Less with Scala
Scalapeno18 - Thinking Less with Scala
 
Clojure And Swing
Clojure And SwingClojure And Swing
Clojure And Swing
 
Scala is java8.next()
Scala is java8.next()Scala is java8.next()
Scala is java8.next()
 
Chapter 02 functions -class xii
Chapter 02   functions -class xiiChapter 02   functions -class xii
Chapter 02 functions -class xii
 
Scala ntnu
Scala ntnuScala ntnu
Scala ntnu
 
Practical cats
Practical catsPractical cats
Practical cats
 
Functional Programming You Already Know
Functional Programming You Already KnowFunctional Programming You Already Know
Functional Programming You Already Know
 
C# programming
C# programming C# programming
C# programming
 
Spark workshop
Spark workshopSpark workshop
Spark workshop
 

Más de Fabernovel

GAFAnomics Tesla Volume 2 - Is Tesla the disruptor we need?
GAFAnomics Tesla Volume 2 -  Is Tesla the disruptor we need?GAFAnomics Tesla Volume 2 -  Is Tesla the disruptor we need?
GAFAnomics Tesla Volume 2 - Is Tesla the disruptor we need?Fabernovel
 
Lancement de ReCOVery - Sortie de crise - Les nouveaux raisonnables
Lancement de ReCOVery - Sortie de crise - Les nouveaux raisonnablesLancement de ReCOVery - Sortie de crise - Les nouveaux raisonnables
Lancement de ReCOVery - Sortie de crise - Les nouveaux raisonnablesFabernovel
 
[Extract] Study - Talent KPIs
[Extract] Study - Talent KPIs [Extract] Study - Talent KPIs
[Extract] Study - Talent KPIs Fabernovel
 
Fabernovel analyse les tendances publicitaires du super bowl de 2020
Fabernovel analyse les tendances publicitaires du super bowl de 2020Fabernovel analyse les tendances publicitaires du super bowl de 2020
Fabernovel analyse les tendances publicitaires du super bowl de 2020Fabernovel
 
Amazon is eating the world
Amazon is eating the worldAmazon is eating the world
Amazon is eating the worldFabernovel
 
Gafanomics - Quarterly - Episode 4 (Q4FY19)
Gafanomics - Quarterly - Episode 4 (Q4FY19) Gafanomics - Quarterly - Episode 4 (Q4FY19)
Gafanomics - Quarterly - Episode 4 (Q4FY19) Fabernovel
 
Fabernovel study on SoftBank
Fabernovel study on SoftBankFabernovel study on SoftBank
Fabernovel study on SoftBankFabernovel
 
The new retail: apocalypse or new era?
The new retail: apocalypse or new era?The new retail: apocalypse or new era?
The new retail: apocalypse or new era?Fabernovel
 
L’aventure iOS - tvOS myCANAL
L’aventure iOS - tvOS myCANALL’aventure iOS - tvOS myCANAL
L’aventure iOS - tvOS myCANALFabernovel
 
WeChat Social Ads Playbook
WeChat Social Ads PlaybookWeChat Social Ads Playbook
WeChat Social Ads PlaybookFabernovel
 
[Extract] Study The We Company: is real estate a disruptable industry?
[Extract] Study The We Company: is real estate a disruptable industry?[Extract] Study The We Company: is real estate a disruptable industry?
[Extract] Study The We Company: is real estate a disruptable industry?Fabernovel
 
Insight Report by Fabernovel - The Hidden consumption force of Overseas Chine...
Insight Report by Fabernovel - The Hidden consumption force of Overseas Chine...Insight Report by Fabernovel - The Hidden consumption force of Overseas Chine...
Insight Report by Fabernovel - The Hidden consumption force of Overseas Chine...Fabernovel
 
Study Ardian & Fabernovel - The Augmented Infrastructure: Digital for climate?
Study Ardian & Fabernovel - The Augmented Infrastructure:  Digital for climate?Study Ardian & Fabernovel - The Augmented Infrastructure:  Digital for climate?
Study Ardian & Fabernovel - The Augmented Infrastructure: Digital for climate?Fabernovel
 
Services urbains : faut il vraiment penser utilisateur ?
Services urbains : faut il vraiment penser utilisateur ?Services urbains : faut il vraiment penser utilisateur ?
Services urbains : faut il vraiment penser utilisateur ?Fabernovel
 
[Fabernovel study] New economy, new KPI: the customer era
[Fabernovel study] New economy, new KPI:  the customer era[Fabernovel study] New economy, new KPI:  the customer era
[Fabernovel study] New economy, new KPI: the customer eraFabernovel
 
Gafanomics - The Quarterly - Episode 2 (Q2FY19)
Gafanomics - The Quarterly - Episode 2 (Q2FY19)Gafanomics - The Quarterly - Episode 2 (Q2FY19)
Gafanomics - The Quarterly - Episode 2 (Q2FY19)Fabernovel
 
The Future of Corporate Learning: from Training to Learning Experience
The Future of Corporate Learning: from Training to Learning ExperienceThe Future of Corporate Learning: from Training to Learning Experience
The Future of Corporate Learning: from Training to Learning ExperienceFabernovel
 
Gafanomics - The Quarterly - Episode 1 (Q1FY19)
Gafanomics - The Quarterly - Episode 1 (Q1FY19)Gafanomics - The Quarterly - Episode 1 (Q1FY19)
Gafanomics - The Quarterly - Episode 1 (Q1FY19)Fabernovel
 
Vers une nouvelle ère de vos expériences
Vers une nouvelle ère de vos expériencesVers une nouvelle ère de vos expériences
Vers une nouvelle ère de vos expériencesFabernovel
 
Slack, the future workplace
Slack, the future workplaceSlack, the future workplace
Slack, the future workplaceFabernovel
 

Más de Fabernovel (20)

GAFAnomics Tesla Volume 2 - Is Tesla the disruptor we need?
GAFAnomics Tesla Volume 2 -  Is Tesla the disruptor we need?GAFAnomics Tesla Volume 2 -  Is Tesla the disruptor we need?
GAFAnomics Tesla Volume 2 - Is Tesla the disruptor we need?
 
Lancement de ReCOVery - Sortie de crise - Les nouveaux raisonnables
Lancement de ReCOVery - Sortie de crise - Les nouveaux raisonnablesLancement de ReCOVery - Sortie de crise - Les nouveaux raisonnables
Lancement de ReCOVery - Sortie de crise - Les nouveaux raisonnables
 
[Extract] Study - Talent KPIs
[Extract] Study - Talent KPIs [Extract] Study - Talent KPIs
[Extract] Study - Talent KPIs
 
Fabernovel analyse les tendances publicitaires du super bowl de 2020
Fabernovel analyse les tendances publicitaires du super bowl de 2020Fabernovel analyse les tendances publicitaires du super bowl de 2020
Fabernovel analyse les tendances publicitaires du super bowl de 2020
 
Amazon is eating the world
Amazon is eating the worldAmazon is eating the world
Amazon is eating the world
 
Gafanomics - Quarterly - Episode 4 (Q4FY19)
Gafanomics - Quarterly - Episode 4 (Q4FY19) Gafanomics - Quarterly - Episode 4 (Q4FY19)
Gafanomics - Quarterly - Episode 4 (Q4FY19)
 
Fabernovel study on SoftBank
Fabernovel study on SoftBankFabernovel study on SoftBank
Fabernovel study on SoftBank
 
The new retail: apocalypse or new era?
The new retail: apocalypse or new era?The new retail: apocalypse or new era?
The new retail: apocalypse or new era?
 
L’aventure iOS - tvOS myCANAL
L’aventure iOS - tvOS myCANALL’aventure iOS - tvOS myCANAL
L’aventure iOS - tvOS myCANAL
 
WeChat Social Ads Playbook
WeChat Social Ads PlaybookWeChat Social Ads Playbook
WeChat Social Ads Playbook
 
[Extract] Study The We Company: is real estate a disruptable industry?
[Extract] Study The We Company: is real estate a disruptable industry?[Extract] Study The We Company: is real estate a disruptable industry?
[Extract] Study The We Company: is real estate a disruptable industry?
 
Insight Report by Fabernovel - The Hidden consumption force of Overseas Chine...
Insight Report by Fabernovel - The Hidden consumption force of Overseas Chine...Insight Report by Fabernovel - The Hidden consumption force of Overseas Chine...
Insight Report by Fabernovel - The Hidden consumption force of Overseas Chine...
 
Study Ardian & Fabernovel - The Augmented Infrastructure: Digital for climate?
Study Ardian & Fabernovel - The Augmented Infrastructure:  Digital for climate?Study Ardian & Fabernovel - The Augmented Infrastructure:  Digital for climate?
Study Ardian & Fabernovel - The Augmented Infrastructure: Digital for climate?
 
Services urbains : faut il vraiment penser utilisateur ?
Services urbains : faut il vraiment penser utilisateur ?Services urbains : faut il vraiment penser utilisateur ?
Services urbains : faut il vraiment penser utilisateur ?
 
[Fabernovel study] New economy, new KPI: the customer era
[Fabernovel study] New economy, new KPI:  the customer era[Fabernovel study] New economy, new KPI:  the customer era
[Fabernovel study] New economy, new KPI: the customer era
 
Gafanomics - The Quarterly - Episode 2 (Q2FY19)
Gafanomics - The Quarterly - Episode 2 (Q2FY19)Gafanomics - The Quarterly - Episode 2 (Q2FY19)
Gafanomics - The Quarterly - Episode 2 (Q2FY19)
 
The Future of Corporate Learning: from Training to Learning Experience
The Future of Corporate Learning: from Training to Learning ExperienceThe Future of Corporate Learning: from Training to Learning Experience
The Future of Corporate Learning: from Training to Learning Experience
 
Gafanomics - The Quarterly - Episode 1 (Q1FY19)
Gafanomics - The Quarterly - Episode 1 (Q1FY19)Gafanomics - The Quarterly - Episode 1 (Q1FY19)
Gafanomics - The Quarterly - Episode 1 (Q1FY19)
 
Vers une nouvelle ère de vos expériences
Vers une nouvelle ère de vos expériencesVers une nouvelle ère de vos expériences
Vers une nouvelle ère de vos expériences
 
Slack, the future workplace
Slack, the future workplaceSlack, the future workplace
Slack, the future workplace
 

Último

Key Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdfKey Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdfCheryl Hung
 
UiPath Studio Web workshop series - Day 4
UiPath Studio Web workshop series - Day 4UiPath Studio Web workshop series - Day 4
UiPath Studio Web workshop series - Day 4DianaGray10
 
Introduction to RAG (Retrieval Augmented Generation) and its application
Introduction to RAG (Retrieval Augmented Generation) and its applicationIntroduction to RAG (Retrieval Augmented Generation) and its application
Introduction to RAG (Retrieval Augmented Generation) and its applicationKnoldus Inc.
 
Trailblazer Community - Flows Workshop (Session 2)
Trailblazer Community - Flows Workshop (Session 2)Trailblazer Community - Flows Workshop (Session 2)
Trailblazer Community - Flows Workshop (Session 2)Muhammad Tiham Siddiqui
 
Technical SEO for Improved Accessibility WTS FEST
Technical SEO for Improved Accessibility  WTS FESTTechnical SEO for Improved Accessibility  WTS FEST
Technical SEO for Improved Accessibility WTS FESTBillieHyde
 
AI Workshops at Computers In Libraries 2024
AI Workshops at Computers In Libraries 2024AI Workshops at Computers In Libraries 2024
AI Workshops at Computers In Libraries 2024Brian Pichman
 
IT Service Management (ITSM) Best Practices for Advanced Computing
IT Service Management (ITSM) Best Practices for Advanced ComputingIT Service Management (ITSM) Best Practices for Advanced Computing
IT Service Management (ITSM) Best Practices for Advanced ComputingMAGNIntelligence
 
LF Energy Webinar - Unveiling OpenEEMeter 4.0
LF Energy Webinar - Unveiling OpenEEMeter 4.0LF Energy Webinar - Unveiling OpenEEMeter 4.0
LF Energy Webinar - Unveiling OpenEEMeter 4.0DanBrown980551
 
Webinar: The Art of Prioritizing Your Product Roadmap by AWS Sr PM - Tech
Webinar: The Art of Prioritizing Your Product Roadmap by AWS Sr PM - TechWebinar: The Art of Prioritizing Your Product Roadmap by AWS Sr PM - Tech
Webinar: The Art of Prioritizing Your Product Roadmap by AWS Sr PM - TechProduct School
 
20140402 - Smart house demo kit
20140402 - Smart house demo kit20140402 - Smart house demo kit
20140402 - Smart house demo kitJamie (Taka) Wang
 
3 Pitfalls Everyone Should Avoid with Cloud Data
3 Pitfalls Everyone Should Avoid with Cloud Data3 Pitfalls Everyone Should Avoid with Cloud Data
3 Pitfalls Everyone Should Avoid with Cloud DataEric D. Schabell
 
GraphSummit Copenhagen 2024 - Neo4j Vision and Roadmap.pptx
GraphSummit Copenhagen 2024 - Neo4j Vision and Roadmap.pptxGraphSummit Copenhagen 2024 - Neo4j Vision and Roadmap.pptx
GraphSummit Copenhagen 2024 - Neo4j Vision and Roadmap.pptxNeo4j
 
Explore the UiPath Community and ways you can benefit on your journey to auto...
Explore the UiPath Community and ways you can benefit on your journey to auto...Explore the UiPath Community and ways you can benefit on your journey to auto...
Explore the UiPath Community and ways you can benefit on your journey to auto...DianaGray10
 
Oracle Database 23c Security New Features.pptx
Oracle Database 23c Security New Features.pptxOracle Database 23c Security New Features.pptx
Oracle Database 23c Security New Features.pptxSatishbabu Gunukula
 
Where developers are challenged, what developers want and where DevEx is going
Where developers are challenged, what developers want and where DevEx is goingWhere developers are challenged, what developers want and where DevEx is going
Where developers are challenged, what developers want and where DevEx is goingFrancesco Corti
 
2024.03.12 Cost drivers of cultivated meat production.pdf
2024.03.12 Cost drivers of cultivated meat production.pdf2024.03.12 Cost drivers of cultivated meat production.pdf
2024.03.12 Cost drivers of cultivated meat production.pdfThe Good Food Institute
 
Planetek Italia Srl - Corporate Profile Brochure
Planetek Italia Srl - Corporate Profile BrochurePlanetek Italia Srl - Corporate Profile Brochure
Planetek Italia Srl - Corporate Profile BrochurePlanetek Italia Srl
 
Stobox 4: Revolutionizing Investment in Real-World Assets Through Tokenization
Stobox 4: Revolutionizing Investment in Real-World Assets Through TokenizationStobox 4: Revolutionizing Investment in Real-World Assets Through Tokenization
Stobox 4: Revolutionizing Investment in Real-World Assets Through TokenizationStobox
 
Graphene Quantum Dots-Based Composites for Biomedical Applications
Graphene Quantum Dots-Based Composites for  Biomedical ApplicationsGraphene Quantum Dots-Based Composites for  Biomedical Applications
Graphene Quantum Dots-Based Composites for Biomedical Applicationsnooralam814309
 
The New Cloud World Order Is FinOps (Slideshow)
The New Cloud World Order Is FinOps (Slideshow)The New Cloud World Order Is FinOps (Slideshow)
The New Cloud World Order Is FinOps (Slideshow)codyslingerland1
 

Último (20)

Key Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdfKey Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdf
 
UiPath Studio Web workshop series - Day 4
UiPath Studio Web workshop series - Day 4UiPath Studio Web workshop series - Day 4
UiPath Studio Web workshop series - Day 4
 
Introduction to RAG (Retrieval Augmented Generation) and its application
Introduction to RAG (Retrieval Augmented Generation) and its applicationIntroduction to RAG (Retrieval Augmented Generation) and its application
Introduction to RAG (Retrieval Augmented Generation) and its application
 
Trailblazer Community - Flows Workshop (Session 2)
Trailblazer Community - Flows Workshop (Session 2)Trailblazer Community - Flows Workshop (Session 2)
Trailblazer Community - Flows Workshop (Session 2)
 
Technical SEO for Improved Accessibility WTS FEST
Technical SEO for Improved Accessibility  WTS FESTTechnical SEO for Improved Accessibility  WTS FEST
Technical SEO for Improved Accessibility WTS FEST
 
AI Workshops at Computers In Libraries 2024
AI Workshops at Computers In Libraries 2024AI Workshops at Computers In Libraries 2024
AI Workshops at Computers In Libraries 2024
 
IT Service Management (ITSM) Best Practices for Advanced Computing
IT Service Management (ITSM) Best Practices for Advanced ComputingIT Service Management (ITSM) Best Practices for Advanced Computing
IT Service Management (ITSM) Best Practices for Advanced Computing
 
LF Energy Webinar - Unveiling OpenEEMeter 4.0
LF Energy Webinar - Unveiling OpenEEMeter 4.0LF Energy Webinar - Unveiling OpenEEMeter 4.0
LF Energy Webinar - Unveiling OpenEEMeter 4.0
 
Webinar: The Art of Prioritizing Your Product Roadmap by AWS Sr PM - Tech
Webinar: The Art of Prioritizing Your Product Roadmap by AWS Sr PM - TechWebinar: The Art of Prioritizing Your Product Roadmap by AWS Sr PM - Tech
Webinar: The Art of Prioritizing Your Product Roadmap by AWS Sr PM - Tech
 
20140402 - Smart house demo kit
20140402 - Smart house demo kit20140402 - Smart house demo kit
20140402 - Smart house demo kit
 
3 Pitfalls Everyone Should Avoid with Cloud Data
3 Pitfalls Everyone Should Avoid with Cloud Data3 Pitfalls Everyone Should Avoid with Cloud Data
3 Pitfalls Everyone Should Avoid with Cloud Data
 
GraphSummit Copenhagen 2024 - Neo4j Vision and Roadmap.pptx
GraphSummit Copenhagen 2024 - Neo4j Vision and Roadmap.pptxGraphSummit Copenhagen 2024 - Neo4j Vision and Roadmap.pptx
GraphSummit Copenhagen 2024 - Neo4j Vision and Roadmap.pptx
 
Explore the UiPath Community and ways you can benefit on your journey to auto...
Explore the UiPath Community and ways you can benefit on your journey to auto...Explore the UiPath Community and ways you can benefit on your journey to auto...
Explore the UiPath Community and ways you can benefit on your journey to auto...
 
Oracle Database 23c Security New Features.pptx
Oracle Database 23c Security New Features.pptxOracle Database 23c Security New Features.pptx
Oracle Database 23c Security New Features.pptx
 
Where developers are challenged, what developers want and where DevEx is going
Where developers are challenged, what developers want and where DevEx is goingWhere developers are challenged, what developers want and where DevEx is going
Where developers are challenged, what developers want and where DevEx is going
 
2024.03.12 Cost drivers of cultivated meat production.pdf
2024.03.12 Cost drivers of cultivated meat production.pdf2024.03.12 Cost drivers of cultivated meat production.pdf
2024.03.12 Cost drivers of cultivated meat production.pdf
 
Planetek Italia Srl - Corporate Profile Brochure
Planetek Italia Srl - Corporate Profile BrochurePlanetek Italia Srl - Corporate Profile Brochure
Planetek Italia Srl - Corporate Profile Brochure
 
Stobox 4: Revolutionizing Investment in Real-World Assets Through Tokenization
Stobox 4: Revolutionizing Investment in Real-World Assets Through TokenizationStobox 4: Revolutionizing Investment in Real-World Assets Through Tokenization
Stobox 4: Revolutionizing Investment in Real-World Assets Through Tokenization
 
Graphene Quantum Dots-Based Composites for Biomedical Applications
Graphene Quantum Dots-Based Composites for  Biomedical ApplicationsGraphene Quantum Dots-Based Composites for  Biomedical Applications
Graphene Quantum Dots-Based Composites for Biomedical Applications
 
The New Cloud World Order Is FinOps (Slideshow)
The New Cloud World Order Is FinOps (Slideshow)The New Cloud World Order Is FinOps (Slideshow)
The New Cloud World Order Is FinOps (Slideshow)
 

Talk - Query monad

  • 1. QUERY MONAD:QUERY MONAD: A SPECIALIZED READER FOR PURE DATABASEA SPECIALIZED READER FOR PURE DATABASE PROGRAMMINGPROGRAMMING STÉPHANE TANKOUA (GITHUB: @STANKOUA / TWITTER: @STEFSHINE)STÉPHANE TANKOUA (GITHUB: @STANKOUA / TWITTER: @STEFSHINE) SCALA.IO 2018SCALA.IO 2018 1
  • 9. CONTEXT: TECH STACKCONTEXT: TECH STACK Technologies we commonly use: Play (web) Anorm (persistence) Relational database (PostgreSQL) 4
  • 10. FOCUSING ON ANORMFOCUSING ON ANORM How to use it (from of cial documentation) import anorm._ def one(implicit c: java.sql.Connection): Int = { val query: SimpleSql = SQL("select 1 as res") query.as(SqlParser.int("res").single) } 5
  • 11. OUR AIMOUR AIM What could be improved ? 6
  • 12. OUR AIMOUR AIM What could be improved ? Not blocking the current thread 6
  • 13. OUR AIMOUR AIM What could be improved ? Not blocking the current thread Representing effect as type (DB read is a side-effect) 6
  • 14. OUR AIMOUR AIM What could be improved ? Not blocking the current thread Representing effect as type (DB read is a side-effect) Doesn't compose 6
  • 15. OUR AIMOUR AIM What could be improved ? Not blocking the current thread Representing effect as type (DB read is a side-effect) Doesn't compose Purity (Referential Transparency + no side-effects) 6
  • 17. An expression is called referentially transparent if it can be replaced with its corresponding value without changing the program's behavior. 8 . 1
  • 18. REFERENTIAL TRANSPARENCYREFERENTIAL TRANSPARENCY EXAMPLEEXAMPLE is equivalent to: def inc(x: Int): Int = x + 1 val res = inc(5) + inc(5) // console:> res: Int = 12 val res = 6 + 6 // Replacing inc(5) by its value // console:> res: Int = 12 8 . 2
  • 19. REFERENTIAL TRANSPARENCYREFERENTIAL TRANSPARENCY BACK TO ANORM EXAMPLEBACK TO ANORM EXAMPLE is NOT equivalent to: Because the database will not be called implicit val c: Connection = ... def one(implicit c: Connection): Int = { val query: SimpleQuery = SQL("select 1 as res") query.as(SqlParser.str("res").single) } val res = one // console:> res: Int = 1 val res = 1 // Replacing one by its value // console:> res: Int = 1 8 . 3
  • 23. PRINCIPLESPRINCIPLES Functional principles to rely on: separating representation from interpretation case class Plus(x: Int, y: Int) def run(op: Plus): Int = op.x + op.y val op = Plus(1, 2) // console:> op: Plus = Plus(1,2) run(op) // console:> res0: Int = 3 11 . 2
  • 24. PRINCIPLESPRINCIPLES Functional principles to rely on: separating representation from their interpretation composition // Composition with For-comprehensions (ie monadic) val zipped = for { x <- List(1, 2, 3) y <- List("a", "b", "c") } yield (x, y) // console:> zipped: List[Int] = List((1, "a"), (2, "b"), (3, "c")) 11 . 3
  • 25. PRINCIPLESPRINCIPLES Functional principles to rely on: separating representation from interpretation composition lazy evaluation val stream = 100 #:: 200 #:: 85 #:: Stream.empty val doubledStream = stream.map(_ * 2) // do nothing doubledStream.print() // console:> 200, 400, 170, empty 11 . 4
  • 26. PRINCIPLESPRINCIPLES Functional principles to rely on: separating representation from interpretation composition lazy evaluation 11 . 5
  • 27. THE PURPOSETHE PURPOSE Solution: Using a lazy representational structure which compose (F) case class User(id: Int, name: String, age: Int) def readUser(id: Int): F[User] def updateUser(user: User): F[Unit] // Composing DB atomic operations into a single one def changeUserName(id: Int, newName: String): F[Unit] = for { user <- readUser(id) newUser = user.copy(name = newName) result <- updateUser(newUser) } yield result 12 . 1
  • 29. THE PURPOSETHE PURPOSE Lazy representational structure => interpreter needed val op = changeUserName(1, "John Doe") val result: Future[Unit] = interpreter.run(op) 12 . 2
  • 30. THE PURPOSETHE PURPOSE Lazy representational structure => interpreter needed Interpretation process: val op = changeUserName(1, "John Doe") val result: Future[Unit] = interpreter.run(op) 12 . 2
  • 31. THE PURPOSETHE PURPOSE Lazy representational structure => interpreter needed Interpretation process: 1. Provide context val op = changeUserName(1, "John Doe") val result: Future[Unit] = interpreter.run(op) 12 . 2
  • 32. THE PURPOSETHE PURPOSE Lazy representational structure => interpreter needed Interpretation process: 1. Provide context 2. Execute our operation (within our effect) val op = changeUserName(1, "John Doe") val result: Future[Unit] = interpreter.run(op) 12 . 2
  • 33. THE PURPOSETHE PURPOSE Lazy representational structure => interpreter needed Interpretation process: 1. Provide context 2. Execute our operation (within our effect) 3. Translate it into another effect (F -> Future) val op = changeUserName(1, "John Doe") val result: Future[Unit] = interpreter.run(op) 12 . 2
  • 34. SHAPING OUR SOLUTIONSHAPING OUR SOLUTION import anorm.SimpleSql def readUser(id: Int)(implicit c: Connection): SimpleSql[User] = SQL(s"select * from user where id = $id") def updateUser(user: User)(implicit c: Connection): SimpleSql[Unit] = SQL(s"update user set ...") // NO: doesn't compose // def changeUserName(id: Int, newName: String): SimpleSql[Unit] = for { // user <- readUser(id) // newUser = user.copy(name = newUser) // result <- updateUser(user) // } yield result 13 . 1
  • 35. SHAPING OUR SOLUTIONSHAPING OUR SOLUTION def readUser(id: Int): Future[User] = Future(SQL(s"select * from user where id = $id").as(...)) def updateUser(user: User): Future[Unit] = Future(SQL(s"update user set ...").execute()) // commit when ??? def changeUserName(id:Int, newName:String)(implicit c:Connection): Future[Unit] = for { user <- readUser(id) newUser = user.copy(name = newName) result <- updateUser(newUser) } yield result 13 . 2
  • 36. SHAPING OUR SOLUTIONSHAPING OUR SOLUTION Desiging our lazy representational structure which compose: import java.sql.Connection case class Query[A](f: Connection => A) { def map[B](f: A => B): Query[B] def flatMap[B](f: A => Query[B]): Query[B] } 13 . 3
  • 37. SHAPING OUR SOLUTIONSHAPING OUR SOLUTION How could we use it ? def one: Query[Int] = Query { implicit c => SQL("Select 1 as one").as(SqlParser.str("one").single) } 13 . 4
  • 38. ABOUT THE PRINCIPLESABOUT THE PRINCIPLES Real word example (create a user's bank account): case class User(id: String, name: String) case class Account(id: String, userId: String, balance: BigDecimal) def createUser(user: User): Query[Unit] = ??? def createAccount(account: Account, userId: String): Query[Unit] = ??? def createUserWithAccount(user: User, account: Account): Query[Unit] = for { // composing operations _ <- createUser(user) _ <- createAccount(account, user.id) } yield () // transactional val op: Query[Unit] = createUserWithAccount(User("1", "John Doe"), Account("a1", "1", 100)) // Query[Unit] => lazy => purity !!! 14 . 1
  • 39. ABOUT THE PRINCIPLESABOUT THE PRINCIPLES Real word example (create a user's bank account): val runner = ??? runner(op) // we run the operations (caution: side-effect) // Do it at end of your program // Could not compose anymore 14 . 2
  • 40. QUERY IMPLEMENTATIONQUERY IMPLEMENTATION Idea: Could we link it to an existing one ? 15
  • 41. QUERY IMPLEMENTATIONQUERY IMPLEMENTATION Idea: Could we link it to an existing one ? From signature: a wrapper over a function (of 1 arg) case class Query[A](f: Connection => A) 15
  • 44. INTRODUCING READERINTRODUCING READER Reader: monad which pass along a context From signature: a wrapper over a function (of 1 arg) class Reader[-A, +B](f: A => B) 18 . 1
  • 45. INTRODUCING READERINTRODUCING READER Reader: monad which pass along a context From signature: a wrapper over a function (of 1 arg) map == function composition (g andThen f) class Reader[-A, +B](f: A => B) 18 . 1
  • 46. INTRODUCING READERINTRODUCING READER Reader: monad which pass along a context From signature: a wrapper over a function (of 1 arg) map == function composition (g andThen f) atMap == function composition with wrapping / unwrapping class Reader[-A, +B](f: A => B) 18 . 1
  • 47. INTRODUCING READERINTRODUCING READER Implementation could be easily derived: case class Reader[-A, +B](f: A => B) { def map[C](g: B => C): Reader[A, C] = Reader(g compose f) // function composition def flatMap[C](g: B => Reader[A, C]): Reader[A, C] = Reader(a => g(f(a)).f(a)) // composition & unwrapping } object Reader { def pure[C, A](a: A): Reader[C, A] = Reader(_ => a) // Get only the context def ask[A]: Reader[A, A] = Reader(identity) } 18 . 2
  • 48. DEDUCING IMPLEMENTATIONDEDUCING IMPLEMENTATION Query could be implemented in term of Reader: type Query[A] = Reader[Connection, A] object Query { def pure[A](a: A) = Reader.pure[Connection, A](a) def apply[A](f: Connection => A) = new Query(f) } 19
  • 49. HANDLING MONAD STACKHANDLING MONAD STACK Recurrent problem with monad: Query == Reader How to flatMap over Reader stack ? def divideBy2(x: Int): Query[Option[Int]] def divideBy3(x: Int): Query[Option[Int]] // Query is monad, Option is a monad // but Query[Option[_]] is not // how to compose Query[Option[_]] without nesting ? def divideBy6(x: Int): Query[Option[Int]] = for { y <- divideBy2(x) z <- divideBy3(y) // DOES NOT COMPILE: divideBy3 expect Int, y is Option[Int] } yield z 20
  • 50. TRANSFORMERSTRANSFORMERS One solution: Monad transformer Transformer for Reader: ReaderT /** @param F the monad we add to our stack */ case class ReaderT[F[_], -A, +B](f: A => F[B]) { def map[C](g: B => C)(implicit F: Functor[F]): ReaderT[F, A, C] = ReaderT(a => F.map(f(a))(g)) def flatMap[C](g: B => ReaderT[F, A, C])( implicit M: Monad[F] ): ReaderT[F, A, C] = ReaderT(a => M.flatMap(f(a))(b => g(b).f(a))) } 21 . 1
  • 51. TRANSFORMERSTRANSFORMERS Bonus: Reader in terms of ReaderT type Id[A] = A type Reader[C, A] = ReaderT[Id, C, A] 21 . 2
  • 52. QUERYT TRANSFORMERQUERYT TRANSFORMER type QueryT[F[_], A] = ReaderT[F, Connection, A] type Query[A] = QueryT[Id, A] object QueryT { def apply[M[_], A](run: Connection => M[A]):QueryT[M, A] = new QueryT(run) def pure[M[_]: Applicative, A](a: A) = ReaderT.pure[M, Connection, A](a) def liftF[M[_], A](ma: M[A]) = ReaderT.liftF[M, Connection, A](ma) def fromQuery[M[_], A](query: Query[M[A]]): QueryT[M, A] = QueryT[M, A](query.run) } 22
  • 56. GETTING CONTEXTGETTING CONTEXT Pre-requisite: how to get the context ? 25
  • 57. GETTING CONTEXTGETTING CONTEXT Pre-requisite: how to get the context ? Need to abstract the way we get the connection 25
  • 58. GETTING CONTEXTGETTING CONTEXT Pre-requisite: how to get the context ? Need to abstract the way we get the connection solution: Loan pattern trait WithResource { def apply[A](f: Connection => Future[A]): Future[A] } 25
  • 59. GETTING CONTEXTGETTING CONTEXT Pre-requisite: how to get the context ? Need to abstract the way we get the connection solution: Loan pattern User need to implement it and pass it when trying to interpret a query trait WithResource { def apply[A](f: Connection => Future[A]): Future[A] } 25
  • 60. RESOURCE LOANER EXAMPLERESOURCE LOANER EXAMPLE val loaner = new WithResource { def apply[A](f: Connection => Future[A]): Future[A] = { val conn: Connection = DriverManager.getConnection(URL, USER, PASSWD) f(conn) .andThen { case Success(x) => connection.commit() x case Failure(ex) => connection.rollback() } .andThen { case _ => connection.close() } } } 26
  • 62. NAIVE INTERPRETERNAIVE INTERPRETER Naive runner implementation class QueryRunner(wr: WithResource)(implicit ec: ExecutionContext) { def apply[M[_], T](query: QueryT[M, T]): Future[M[T]] = wr { connection => Future(query.f(connection)) } } 27
  • 63. NAIVE INTERPRETERNAIVE INTERPRETER Naive runner implementation Edge case: M == Future class QueryRunner(wr: WithResource)(implicit ec: ExecutionContext) { def apply[M[_], T](query: QueryT[M, T]): Future[M[T]] = wr { connection => Future(query.f(connection)) } } 27
  • 64. NAIVE INTERPRETERNAIVE INTERPRETER Naive runner implementation Edge case: M == Future The connection will be closed before the termination of our operation class QueryRunner(wr: WithResource)(implicit ec: ExecutionContext) { def apply[M[_], T](query: QueryT[M, T]): Future[M[T]] = wr { connection => Future(query.f(connection)) } } 27
  • 65. REFINED INTERPRETERREFINED INTERPRETER How to handle composition with asynchronous effects ? Solution: A typeclass which handle composition trait ComposeWithCompletion[F[_], Out] { type Outer def apply[In](loaner: WithResource[In], f: In => F[Out]): Future[Outer] } 28 . 1
  • 66. REFINED INTERPRETERREFINED INTERPRETER Two instances of ComposeWithCompletion: one to compose on Future implicit def futureOut[A] = // asynchronous effect new ComposeWithCompletion[Future, A] { type Outer = A def apply[In]( loaner: WithResource[In], f: In => Future[A] ): Future[Outer] = loaner(f) } 28 . 2
  • 67. REFINED INTERPRETERREFINED INTERPRETER Two instances of ComposeWithCompletion: one for other effects (with low priority) implicit def pureOut[F[_], A] = // synchronous effects new ComposeWithCompletion[F, A] { type Outer = F[A] def apply[In](loaner: WithResource[In], f: In => F[A]): Future[Outer] = loaner(r => Future(f(r))) } 28 . 3
  • 68. REFINED INTERPRETERREFINED INTERPRETER Two instances of ComposeWithCompletion: one to compose on Future one for other effects (with low priority) trait LowPriorityCompose { implicit def pureOut[F[_], A] = ??? } object ComposeWithCompletion extends LowPriorityCompose { implicit def futureOut[A] = ??? } 28 . 4
  • 69. COMPLETECOMPLETE QUERYRUNNERQUERYRUNNER Updating our QueryRunner: class QueryRunner(wr: WithResource)( implicit ec: ExecutionContext ) { def apply[M[_], T](query: QueryT[M, T])( implicit cmp: ComposeWithCompletion[M, T] ): Future[cmp.Outer] = compose(wr, query.run) } 29
  • 70. NOTE ON ASYNC EFFECTNOTE ON ASYNC EFFECT case class User(id: Int, email: String) def saveUser(user: User): Query[Unit] = ??? def sendNotificationEmail(userEmail: String): Future[Unit] = ??? // DO NOT DO THIS => Not atomic !!!!!!!! def createAndNotifyUser(user: User): QueryT[Future, Unit] = ??? // for { // _ <- saveUser(user).map(Future.successful) // _ <- QueryT(_ => sendNotificationEmail(user.email)) // } 30
  • 71. RESOURCE ABSTRACTIONRESOURCE ABSTRACTION Could we abstract query to handle resource other than java.sql.Connection type Query[Resource, A] = Reader[Resource, A] type QueryT[F[_], Resource, A] = ReaderT[F, Resource, A] trait WithResource[Resource] { def apply[A](f: Resource => A): A } // Query aliases type SqlQuery[A] = Query[Connection, A] type SqlQueryT[F[_], A] = QueryT[F, Connection, A] 31
  • 74. QUERY-MONAD LIBRARYQUERY-MONAD LIBRARY compiled into a library: open-source (contribution accepted ;-) ) modulable and extensible based on cats https://github.com/zengularity/query- monad 34
  • 75. FEATURES:FEATURES: abstracted Query type Query[Resource, A] = Reader[Resource, A] type QueryT[F[_], Resource, A] = ReaderT[F, Resource, A] type QueryO[Resource, A] = QueryT[Option, Resource, A] type QueryE[Resource, Err, A] = QueryT[Either[Err, ?], Resource, A] 35
  • 76. FEATURES:FEATURES: aliases for sql databases (context == java.sql.Connection) import java.sql.Connection type SqlQuery[A] = Query[Connection, A] type SqlQueryT[F[_], A] = QueryT[F, Connection, A] type SqlQueryO[A] = QueryO[Connection, A] type SqlQueryE[A, Err] = QueryE[Connection, A, Err] // aliases for interpreter type WithSqlConnection = WithResource[Connection] type SqlQueryRunner = QueryRunner[Connection] 36
  • 77. FEATURES:FEATURES: Resource loaner implementation for Play 2 import java.sql.Connection import play.api.db.Database class WithPlayTransaction(db: Database) extends WithSqlConnection { def apply[A](f: Connection => A): A = { ... } } 37
  • 78. QUESTIONS ?QUESTIONS ? (I HOPE NO)(I HOPE NO) BUT HOPE YOUBUT HOPE YOU ITIT 38