SlideShare a Scribd company logo
1 of 137
Download to read offline
Trip with monads
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 1
Why should I write FP code?
1. Modularity
2. Testability
3. Performance
4. Maintainability
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 2
FP Building Blocks
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 3
FP Building Blocks
case class EitherT[F[_], A, B](
run: F[A / B]
)
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 4
FP Building Blocks
case class EitherT[F[_], A, B](
run: F[A / B]
)
A / B
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 5
FP Building Blocks
case class EitherT[F[_], A, B](
run: F[A / B]
)
A __/ B
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 6
FP Building Blocks
case class EitherT[F[_], A, B](
run: F[A / B]
)
A _( )_/ B
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 7
FP Building Blocks
case class EitherT[F[_], A, B](
run: F[A / B]
)
A ¯_( )_/¯ B
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 8
FP Building Blocks
case class Reader[A, B](
run: A => B
)
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 9
FP Building Blocks
case class Kleisli[M[_], A, B](
run: A => M[B]
)
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 10
FP Building Blocks
case class Kleisli[M[_], A, B](
run: A => M[B]
)
type ReaderT[M[_], A, B] = Kleisli[M, A, B]
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 11
FP Building Blocks
case class Kleisli[M[_], A, B](
run: A => M[B]
)
type ReaderT[M[_], A, B] = Kleisli[M, A, B]
type Reader[A, B] = Kleisli[Id, A, B]
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 12
FP Building Blocks
case class Kleisli[M[_], A, B](
run: A => M[B]
)
type ReaderT[M[_], A, B] = Kleisli[M, A, B]
type Reader[A, B] = Kleisli[Id, A, B]
type Id[A] = A
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 13
FP Building Blocks
case class State[S, A](
run: S => (S, A)
)
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 14
FP Building Blocks
case class StateT[M[_], S, A](
run: S => M[(S, A)]
)
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 15
FP Building Blocks
case class StateT[M[_], S, A](
run: S => M[(S, A)]
)
type State[A, B] = StateT[Id, A, B]
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 16
FP Building Blocks
case class EitherT[F[_], A, B](run: F[A / B])
case class Kleisli[M[_], A, B](run: A => M[B])
type ReaderT[M[_], A, B] = Kleisli[M, A, B]
type Reader[A, B] = Kleisli[Id, A, B]
case class StateT[M[_], S, A](run: S => M[(S, A)])
type State[A, B] = StateT[Id, A, B]
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 17
And now what?
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 18
Learning Functional Programming
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 19
Let's write FP application!
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 20
The App
>
.
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 21
The App
> run
.
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 22
The App
> run
Using weather service at http://our-super-weather-forcast-app.com
What is the next city?
.
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 23
The App
> run
Using weather service at http://our-super-weather-forcast-app.com
What is the next city?
Wroclaw
.
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 24
The App
> run
Using weather service at http://our-super-weather-forcast-app.com
What is the next city?
Wroclaw
Forcast for City(Wroclaw) is Temperature(28, Celcius)
Hottest city found so far is (City(Wroclaw), Temperature(28, Celcius))
What is the next city?
.
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 25
The App
> run
Using weather service at http://our-super-weather-forcast-app.com
What is the next city?
Wroclaw
Forcast for City(Wroclaw) is Temperature(28, Celcius)
Hottest city found so far is (City(Wroclaw), Temperature(28, Celcius))
What is the next city?
London
.
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 26
The App
> run
Using weather service at http://our-super-weather-forcast-app.com
What is the next city?
Wroclaw
Forcast for City(Wroclaw) is Temperature(28, Celcius)
Hottest city found so far is (City(Wroclaw), Temperature(28, Celcius))
What is the next city?
London
Forcast for City(London) is Temperature(34, Celcius)
Hottest city found so far is (City(London), Temperature(34, Celcius))
What is the next city?
.
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 27
The App
> run
Using weather service at http://our-super-weather-forcast-app.com
What is the next city?
Wroclaw
Forcast for City(Wroclaw) is Temperature(28, Celcius)
Hottest city found so far is (City(Wroclaw), Temperature(28, Celcius))
What is the next city?
London
Forcast for City(London) is Temperature(34, Celcius)
Hottest city found so far is (City(London), Temperature(34, Celcius))
What is the next city?
kjsdkjssd
.
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 28
The App
> run
Using weather service at http://our-super-weather-forcast-app.com
What is the next city?
Wroclaw
Forcast for City(Wroclaw) is Temperature(28, Celcius)
Hottest city found so far is (City(Wroclaw), Temperature(28, Celcius))
What is the next city?
London
Forcast for City(London) is Temperature(34, Celcius)
Hottest city found so far is (City(London), Temperature(34, Celcius))
What is the next city?
kjsdkjssd
Encountered an error: UknownCity(kjsdkjssd)
>
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 29
Domain
sealed trait TempUnit
case object Celcius extends TempUnit
case object Fahren extends TempUnit
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 30
Domain
case class Temperature(
value: Int,
unit: TempUnit = Celcius
)
case class Forcast(temperature: Temperature)
case class City(name: String)
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 31
Third-party
class WeatherClient(host: String, port: Int) {
def forcast(city: City): Forcast = city match {
case City("Wroclaw") =>
Forcast(Temperature(28))
case City("London") =>
Forcast(Temperature(34))
}
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 32
Helper classes
case class Config(host: String, port: Int)
sealed trait Error
case class UnknownCity(city: String) extends Error
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 33
Helper classes
type Requests = Map[City, Forcast]
object Requests {
def empty: Requests = Map.empty[City, Forcast]
def hottest(requests: Requests): (City, Forcast) = ...
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 34
Imports
import scalaz._, Scalaz._
import monix.eval.Task
import monix.execution.Scheduler
import shims._
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 35
So what do we need?
1. Host & port
2. Input / output
3. City by name (errors)
4. Forcast from third-party
5. Hottest city
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 36
Host & port
def host: Reader[Config, String] =
Reader(_.host)
def port: Reader[Config, Int] =
Reader(_.port)
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 37
Input / output
def printLn(line: String): Task[Unit] =
Task.delay(println(line))
def readLn: Task[String] =
Task.delay(scala.io.StdIn.readLine)
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 38
City by name
def cityByName(cityName: String): Error / City =
cityName match {
case "Wroclaw" =>
/-(City(cityName))
case "London" =>
/-(City(cityName))
case _ =>
-/(UnknownCity(cityName))
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 39
Weather forcast
def weather(city: City)(
host: String, port: Int
): Task[Forcast] = Task.delay {
new WeatherClient(host, port).forcast(city)
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 40
Hottest city
def hottestCity: State[(City, Temperature)] =
State { reqs =>
val temper = Requests.hottest(reqs)
.map(f => f.temperature)
(reqs, temper)
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 41
Ask city
def askCity: Task[String] = for {
_ <- printLn("What is the next city?")
cityName <- readLn
} yield cityName
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 42
def fetchForcast(city: City)(host: String, port: Int): StateReq[Forcast] =
for {
mForcast <- StateT { (reqs: Requests) =>
Task.delay ((reqs, reqs.get(city)))
}
forcast <- StateT { (reqs: Requests) =>
maybeForcast
.cata(_.pure[Task], weather(city)(host, port))
.map (forcast => (reqs, forcast))
}
_ <- StateT { (reqs: Requests) =>
Task.delay ((reqs + (city -> forcast), ()))
}
} yield forcast
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 43
Final program
def askFetchJudge =
for {
cityName <- askCity
city <- cityByName(cityName)
h <- host
p <- port
forcast <- fetchForcast(city)(host, port)
_ <- printLn(s"Forcast for $city is ${forcast.temperature}")
hottest <- hottestCity
_ <- printLn(s"Hottest city found so far is $hottest")
} yield ()
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 44
Final program
def program = for {
h <- host
p <- port
_ <- printLn(s"Using weather service at http://$h:$p n")
_ <- askFetchJudge.forever
} yield ()
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 45
WRONG!
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 46
Reality
def askFetchJudge: Effect[Unit] =
for {
cityName <- askCity.liftM[ErrorEitherT].liftM[ConfigReaderT].liftM[RequestsStateT]
city <- EitherT
.fromDisjunction[Task](cityByName(cityName))
.liftM[ConfigReaderT].liftM[RequestsStateT]
h <- StateT[Effect1, Requests, String]((requests: Requests) =>
ReaderT[Effect0, Config, (Requests, String)]((config: Config) =>
EitherT(Task.delay {
((requests, host.run(config))).right[Error]
})))
p <- StateT[Effect1, Requests, Int]((requests: Requests) =>
ReaderT[Effect0, Config, (Requests, Int)]((config: Config) =>
EitherT(Task.delay {
((requests, port.run(config))).right[Error]
})))
forcast <- fetchForcast(city)(h, p)
.mapK[Effect1, Forcast, Requests](
(t: Task[(Requests, Forcast)]) =>
ReaderT[EitherT[Task, Error, ?], Config, (Requests, Forcast)](
κ(EitherT[Task, Error, (Requests, Forcast)](
t.map(_.right[Error])))))
_ <- printLn(s"Forcast for $city is ${forcast.temperature}")
.liftM[ErrorEitherT].liftM[ConfigReaderT].liftM[RequestsStateT]
hottest <- hottestCity.mapK[Effect1, (City, Temperature), Requests](
(t: Task[(Requests, (City, Temperature))]) =>
ReaderT[EitherT[Task, Error, ?],
Config,
(Requests, (City, Temperature))](
κ(EitherT[Task, Error, (Requests, (City, Temperature))](
t.map(_.right[Error])))))
_ <- printLn(s"Hottest city found so far is $hottest")
.liftM[ErrorEitherT]
.liftM[ConfigReaderT]
.liftM[RequestsStateT]
} yield ()
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 47
Reality
def program: Effect[Unit] = for {
h <- StateT[Effect1, Requests, String]((requests: Requests) =>
ReaderT[Effect0, Config, (Requests, String)]((config: Config) =>
EitherT(Task.delay {
((requests, host.run(config))).right[Error]
})))
p <- StateT[Effect1, Requests, Int]((requests: Requests) =>
ReaderT[Effect0, Config, (Requests, Int)]((config: Config) =>
EitherT(Task.delay {
((requests, port.run(config))).right[Error]
})))
_ <- printLn(s"Using weather service at http://$h:$p n")
.liftM[ErrorEitherT]
.liftM[ConfigReaderT]
.liftM[RequestsStateT]
_ <- askFetchJudge.forever
} yield ()
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 48
"The FP sounds rather like
a mediæval monk, denying
himself the pleasures of life
in the hope that it will
make him virtuous."
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 49
Modularity
Testability
Performance
Maintainability
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 50
Modularity !
Testability
Performance
Maintainability
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 51
Modularity !
Testability !
Performance
Maintainability
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 52
Modularity !
Testability !
Performance !
Maintainability
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 53
Modularity !
Testability !
Performance !
Maintainability !
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 54
def askFetchJudge: Effect[Unit] = ..
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 55
def askFetchJudge: Effect[Unit] = ..
type Effect0[A] = EitherT[Task, Error, A]
type Effect1[A] = ReaderT[Effect0, Config, A]
type Effect[A] = StateT[Effect1, Requests, A]
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 56
def main(args: Array[String]): Unit = {
val config = Config("localhost", 8080)
val requests = Requests.empty
}
.
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 57
def main(args: Array[String]): Unit = {
val config = Config("localhost", 8080)
val requests = Requests.empty
val app: Effect[Unit] = program
implicit val io: SchedulerService =
Scheduler.io("io-scheduler")
}
.
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 58
def main(args: Array[String]): Unit = {
val config = Config("localhost", 8080)
val requests = Requests.empty
val app: Effect[Unit] = program
implicit val io: SchedulerService =
Scheduler.io("io-scheduler")
app.run(requests).run(config).run
}
.
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 59
def main(args: Array[String]): Unit = {
val config = Config("localhost", 8080)
val requests = Requests.empty
val app: Effect[Unit] = program
implicit val io: SchedulerService =
Scheduler.io("io-scheduler")
app.run(requests).run(config).run >>= {
case -/(error) =>
printLn(s"Encountered an error:" +
s"${error.shows}")
case /-(_) => ().pure[Task]
}
}
.
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 60
def main(args: Array[String]): Unit = {
val config = Config("localhost", 8080)
val requests = Requests.empty
val app: Effect[Unit] = program
implicit val io: SchedulerService =
Scheduler.io("io-scheduler")
(app.run(requests).run(config).run >>= {
case -/(error) =>
printLn(s"Encountered an error:" +
s"${error.shows}")
case /-(_) => ().pure[Task]
}).unsafeRunSync
}
.
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 61
Can we do better?
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 62
So what do we need?
1. Host & port
2. Input / output
3. City by name (errors)
4. Forcast from third-party
5. Hottest city
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 63
Host & port
def host: Reader[Config, String] =
Reader(_.host)
def port: Reader[Config, Int] =
Reader(_.port)
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 64
Host & port
def host[F[_]]: F[String] = ?
def port[F[_]]: F[Int] = ?
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 65
trait ApplicativeAsk[F[_], E] {
val applicative: Applicative[F]
def ask: F[E]
def reader[A](f: E => A): F[A]
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 66
trait ApplicativeAsk[F[_], E] {
val applicative: Applicative[F]
def ask: F[E]
def reader[A](f: E => A): F[A]
}
type ConfigAsk[F[_]] = ApplicativeAsk[F, Config]
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 67
def host[F[_]]: F[String] = ?
def port[F[_]]: F[Int] = ?
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 68
def host[F[_]: ConfigAsk]: F[String] =
ConfigAsk[F].reader(_.host)
def port[F[_]: ConfigAsk]: F[Int] =
ConfigAsk[F].reader(_.port)
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 69
def host[F[_]: ConfigAsk]: F[String] =
ConfigAsk[F].reader(_.host)
def port[F[_]: ConfigAsk]: F[Int] =
ConfigAsk[F].reader(_.port)
trait ApplicativeAsk[F[_], E] {
val applicative: Applicative[F]
def ask: F[E]
def reader[A](f: E => A): F[A]
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 70
City by name
def cityByName(cityName: String): Error / City =
cityName match {
case "Wroclaw" => /-(City(cityName))
case "London" => /-(City(cityName))
case _ => -/(UnknownCity(cityName))
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 71
City by name
def cityByName[F[_]](
cityName: String
): F[City] =
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 72
City by name
def cityByName[F[_]:Applicative](
cityName: String
): F[City] = cityName match {
case "Wroclaw" => City(cityName).pure[F]
case "London" => City(cityName).pure[F]
case _ => ???
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 73
trait ApplicativeError[F[_], E] {
val applicative: Applicative[F]
def raiseError[A](e: E): F[A]
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 74
trait ApplicativeError[F[_], E] {
val applicative: Applicative[F]
def raiseError[A](e: E): F[A]
}
type ErrorHandler[F[_]] = ApplicativeError[F, Error]
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 75
City by name
def cityByName[F[_]:Applicative](
cityName: String
): F[City] = cityName match {
case "Wroclaw" => City(cityName).pure[F]
case "London" => City(cityName).pure[F]
case _ => ???
} .
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 76
City by name
def cityByName[F[_]: Applicative: ErrorHandler](
cityName: String
): F[City] = cityName match {
case "Wroclaw" => City(cityName).pure[F]
case "London" => City(cityName).pure[F]
case _ => ErrorHandler[F].raiseError(UnknownCity(cityName))
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 77
Hottest city
def hottestCity: State[(City, Temperature)] =
State { reqs =>
val temper = Requests.hottest(reqs)
.map(f => f.temperature)
(reqs, temper)
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 78
Hottest city
def hottestCity[F[_]]: F[(City, Temperature)]= ?
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 79
trait MonadState[F[_], S] {
val monad: Monad[F]
def get: F[S]
def set(s: S): F[Unit]
def inspect[A](f: S => A): F[A]
def modify(f: S => S): F[Unit]
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 80
trait MonadState[F[_], S] {
val monad: Monad[F]
def get: F[S]
def set(s: S): F[Unit]
def inspect[A](f: S => A): F[A]
def modify(f: S => S): F[Unit]
}
type RequestsState[F[_]] = MonadState[F, Requests]
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 81
Hottest city
def hottestCity[F[_]]: F[(City, Temperature)]= ?
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 82
Hottest city
def hottestCity[
F[_]: RequestsState
]: F[(City, Temperature)] =
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 83
Hottest city
def hottestCity[
F[_]: RequestsState
]: F[(City, Temperature)] =
RequestsState[F].inspect(reqs =>
Requests.hottest(reqs).map(_.temperature)
)
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 84
Hottest city
def hottestCity[
F[_]: RequestsState
]: F[(City, Temperature)] =
RequestsState[F].inspect(reqs =>
Requests.hottest(reqs).map(_.temperature)
)
trait MonadState[F[_], S] {
// (...)
def inspect[A](f: S => A): F[A]
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 85
MTL© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 86
Input / output
def printLn(line: String): Task[Unit] =
Task.delay(println(line))
def readLn: Task[String] =
Task.delay(scala.io.StdIn.readLine)
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 87
Input / output
trait Console[F[_]] {
def readLine: F[String]
def printLn(line: String): F[Unit]
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 88
Ask City
def askCity[F[_]: Console: Monad]: F[String] =
for {
_ <- Console[F].printLn("What is the next city?")
cityName <- Console[F].readLine
} yield cityName
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 89
Weather forcast
def weather(city: City)(
host: String, port: Int
): Task[Forcast] = Task.delay {
new WeatherClient(host, port).forcast(city)
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 90
Weather forcast
trait Weather[F[_]] {
def forcast(city: City): F[Forcast]
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 91
Fetch forcast
def fetchForcast[F[_]: Weather: RequestsState: Monad](city: City): F[Forcast] =
for {
maybeForcast <- RequestsState[F].inspect(_.get(city))
forcast <- maybeForcast.cata(
_.pure[F],
Weather[F].forcast(city)
)
_ <- RequestsState[F].modify(_ + (city -> forcast))
} yield forcast
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 92
Tagless final
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 93
Final program
def askFetchJudge[F[_]: Console: Weather :
RequestsState: ErrorHandler:Monad]: F[Unit] =
for {
cityName <- askCity[F]
city <- cityByName[F](cityName)
forcast <- fetchForcast[F](city)
_ <- Console[F].printLn(s"Forcast for $city is ${forcast.temperature}")
hottest <- hottestCity[F]
_ <- Console[F].printLn(s"Hottest city found so far is $hottest")
} yield ()
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 94
Final program
def program[F[_]: ConfigAsk: Console: Weather:
RequestsState:ErrorHandler:Monad]: F[Unit] =
for {
h <- host[F]
p <- port[F]
_ <- Console[F].printLn(s"Using weather service at http://$h:$p")
_ <- askFetchJudge[F].forever
} yield ()
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 95
def main(args: Array[String]): Unit = {
val config = Config("localhost", 8080)
val requests = Requests.empty
type Effect[A] = ???
val app: Effect[Unit] = program[Effect]
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 96
def main(args: Array[String]): Unit = {
val config = Config("localhost", 8080)
val requests = Requests.empty
type Effect[A] = Task[A]
val app: Effect[Unit] = program[Effect]
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 97
def main(args: Array[String]): Unit = {
val config = Config("localhost", 8080)
val requests = Requests.empty
type Effect[A] = Task[A]
implicit val weather = ???
val app: Effect[Unit] = program[Effect]
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 98
object Weather {
def monixWeather(config: Config): Weather[Task] =
new Weather[Task] {
val client: WeatherClient = new
WeatherClient(config.host, config.port)
def forcast(city: City): Task[Forcast] =
Task.delay {
client.forcast(city)
}
}
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 99
def main(args: Array[String]): Unit = {
val config = Config("localhost", 8080)
val requests = Requests.empty
type Effect[A] = Task[A]
implicit val weather = ???
val app: Effect[Unit] = program[Effect]
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 100
def main(args: Array[String]): Unit = {
val config = Config("localhost", 8080)
val requests = Requests.empty
type Effect[A] = Task[A]
implicit val weather = Weather.monixWeather(config)
val app: Effect[Unit] = program[Effect]
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 101
def main(args: Array[String]): Unit = {
val config = Config("localhost", 8080)
val requests = Requests.empty
type Effect[A] = Task[A]
implicit val weather = Weather.monixWeather(config)
implicit val console = ???
val app: Effect[Unit] = program[Effect]
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 102
object Console {
val monixConsole: Console[Task] =
new Console[Task] {
def readLine: Task[String] =
Task.delay(scala.io.StdIn.readLine)
def printLn(line: String): Task[Unit] =
Task.delay(println(line))
}
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 103
def main(args: Array[String]): Unit = {
val config = Config("localhost", 8080)
val requests = Requests.empty
type Effect[A] = Task[A]
implicit val weather = Weather.monixWeather(config)
implicit val console = ???
val app: Effect[Unit] = program[Effect]
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 104
def main(args: Array[String]): Unit = {
val config = Config("localhost", 8080)
val requests = Requests.empty
type Effect[A] = Task[A]
implicit val weather = Weather.monixWeather(config)
implicit val console = Console.monixConsole
val app: Effect[Unit] = program[Effect]
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 105
def main(args: Array[String]): Unit = {
val config = Config("localhost", 8080)
val requests = Requests.empty
type Effect[A] = StateT[Task, Requests, A]
implicit val weather = Weather.monixWeather(config)
implicit val console = Console.monixConsole
val app: Effect[Unit] = program[Effect]
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 106
def main(args: Array[String]): Unit = {
val config = Config("localhost", 8080)
val requests = Requests.empty
type Effect1[A] = ReaderT[Task, Config, A]
type Effect[A] = StateT[Effect1, Requests, A]
implicit val weather = Weather.monixWeather(config)
implicit val console = Console.monixConsole
val app: Effect[Unit] = program[Effect]
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 107
def main(args: Array[String]): Unit = {
val config = Config("localhost", 8080)
val requests = Requests.empty
type Effect0[A] = EitherT[Task, Error, A]
type Effect1[A] = ReaderT[Effect0, Config, A]
type Effect[A] = StateT[Effect1, Requests, A]
implicit val weather = Weather.monixWeather(config)
implicit val console = Console.monixConsole
val app: Effect[Unit] = program[Effect]
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 108
def main(args: Array[String]): Unit = {
val config = Config("localhost", 8080)
val requests = Requests.empty
type Effect0[A] = EitherT[Task, Error, A]
type Effect1[A] = ReaderT[Effect0, Config, A]
type Effect[A] = StateT[Effect1, Requests, A]
implicit val weather = Weather.monixWeather(config)
implicit val console = Console.monixConsole
val app: Effect[Unit] = program[Effect]
(app.run(requests).run(config).run >>= {
case -/(error) => console.printLn(s"Encountered an error: ${error.shows}")
case /-(_) => ().pure[Task]
}).unsafeRunSync
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 109
Modularity
Testability
Performance
Maintainability
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 110
Modularity ✅
Testability
Performance
Maintainability
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 111
def askFetchJudge[F[_]: Console: Weather :
RequestsState: ErrorHandler:Monad]: F[Unit] =
for {
cityName <- askCity[F]
city <- cityByName[F](cityName)
forcast <- fetchForcast[F](city)
_ <- Console[F].printLn(s"Forcast for $city is ${forcast.temperature}")
hottest <- hottestCity[F]
_ <- Console[F].printLn(s"Hottest city found so far is $hottest")
} yield ()
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 112
Modularity ✅
Testability
Performance
Maintainability
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 113
Modularity ✅
Testability ✅
Performance
Maintainability
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 114
Modularity ✅
Testability ✅
Performance "
Maintainability
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 115
Modularity ✅
Testability ✅
Performance "
Maintainability ✅
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 116
Can we do better?
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 117
def main(args: Array[String]): Unit = {
val config = Config("localhost", 8080)
val requests = Requests.empty
type Effect0[A] = EitherT[Task, Error, A]
type Effect1[A] = ReaderT[Effect0, Config, A]
type Effect[A] = StateT[Effect1, Requests, A]
implicit val weather = Weather.monixWeather(config)
implicit val console = Console.monixConsole
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 118
private[twm] class AtomicMonadState[S](atomic: Atomic[S])
extends MonadState[Task, S] {
val monad: Monad[Task] = Monad[Task]
def get: Task[S] = Task.delay(atomic.get)
def set(s: S): Task[Unit] = Task.delay(atomic.set(s))
def inspect[A](f: S => A): Task[A] = Task.delay(f(atomic.get))
def modify(f: S => S): Task[Unit] = Task.delay(atomic.transform(f))
}
object AtomicMonadState {
def create[S <: AnyRef](s: S): AtomicMonadState[S] =
new AtomicMonadState(AtomicAny[S](s))
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 119
def main(args: Array[String]): Unit = {
val config = Config("localhost", 8080)
val requests = Requests.empty
type Effect0[A] = EitherT[Task, Error, A]
type Effect1[A] = ReaderT[Effect0, Config, A]
type Effect[A] = StateT[Effect1, Requests, A]
implicit val weather = Weather.monixWeather(config)
implicit val console = Console.monixConsole
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 120
def main(args: Array[String]): Unit = {
val config = Config("localhost", 8080)
val requests = Requests.empty
type Effect0[A] = EitherT[Task, Error, A]
type Effect[A] = ReaderT[Effect0, Config, A]
implicit val weather = Weather.monixWeather(config)
implicit val console = Console.monixConsole
implicit val requestsState =
AtomicMonadState.create(Requests.empty)
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 121
object ApplicativeAsk {
def constant[
F[_]:Applicative, E
](e: E): ApplicativeAsk[F, E] =
new DefaultApplicativeAsk[F, E] {
val applicative: Applicative[F] = Applicative[F]
def ask: F[E] = applicative.point(e)
}
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 122
def main(args: Array[String]): Unit = {
val config = Config("localhost", 8080)
val requests = Requests.empty
type Effect0[A] = EitherT[Task, Error, A]
type Effect[A] = ReaderT[Effect0, Config, A]
implicit val weather = Weather.monixWeather(config)
implicit val console = Console.monixConsole
implicit val requestsState =
AtomicMonadState.create(Requests.empty)
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 123
def main(args: Array[String]): Unit = {
val config = Config("localhost", 8080)
val requests = Requests.empty
type Effect[A] = EitherT[Task, Error, A]
implicit val weather = Weather.monixWeather(config)
implicit val console = Console.monixConsole
implicit val requestsState =
AtomicMonadState.create(Requests.empty)
implicit val configAsk =
ApplicativeAsk.constant[Task, Config](config)
}
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 124
Modularity ✅
Testability ✅
Performance "
Maintainability ✅
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 125
Modularity ✅
Testability ✅
Performance ✅ *
Maintainability ✅
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 126
Can we do better?
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 127
type Effect[A] = EitherT[Task, Error, A]
Last frontier
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 128
Why?
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 129
Why?
1. Performance
2. Two channels error handling
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 130
Introducing IO[E, A]
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 131
Introducing IO[E, A]
https://github.com/scalaz/scalaz-zio
https://github.com/LukaJCB/cats-bio
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 132
Modularity ✅
Testability ✅
Performance ✅ *
Maintainability ✅
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 133
Modularity ✅
Testability ✅
Performance ✅
Maintainability ✅
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 134
Announcements
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 135
Announcements
1. https://github.com/scalaz/scalaz-mtl
2. http://rabbitonweb.com/
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 136
Thank you very much
Pawel Szulc
@rabbitonweb
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 137

More Related Content

Recently uploaded

Connecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdfConnecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdfNeo4j
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsPixlogix Infotech
 
What is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfWhat is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfMounikaPolabathina
 
2024 April Patch Tuesday
2024 April Patch Tuesday2024 April Patch Tuesday
2024 April Patch TuesdayIvanti
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxLoriGlavin3
 
Scale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterScale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterMydbops
 
Generative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdfGenerative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdfIngrid Airi González
 
Modern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better StrongerModern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better Strongerpanagenda
 
Data governance with Unity Catalog Presentation
Data governance with Unity Catalog PresentationData governance with Unity Catalog Presentation
Data governance with Unity Catalog PresentationKnoldus Inc.
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxLoriGlavin3
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024Lonnie McRorey
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxLoriGlavin3
 
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...Alkin Tezuysal
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsSergiu Bodiu
 
[Webinar] SpiraTest - Setting New Standards in Quality Assurance
[Webinar] SpiraTest - Setting New Standards in Quality Assurance[Webinar] SpiraTest - Setting New Standards in Quality Assurance
[Webinar] SpiraTest - Setting New Standards in Quality AssuranceInflectra
 
Assure Ecommerce and Retail Operations Uptime with ThousandEyes
Assure Ecommerce and Retail Operations Uptime with ThousandEyesAssure Ecommerce and Retail Operations Uptime with ThousandEyes
Assure Ecommerce and Retail Operations Uptime with ThousandEyesThousandEyes
 
Rise of the Machines: Known As Drones...
Rise of the Machines: Known As Drones...Rise of the Machines: Known As Drones...
Rise of the Machines: Known As Drones...Rick Flair
 
Take control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteTake control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteDianaGray10
 
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...Wes McKinney
 
Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...
Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...
Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...Scott Andery
 

Recently uploaded (20)

Connecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdfConnecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdf
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and Cons
 
What is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfWhat is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdf
 
2024 April Patch Tuesday
2024 April Patch Tuesday2024 April Patch Tuesday
2024 April Patch Tuesday
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
 
Scale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterScale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL Router
 
Generative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdfGenerative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdf
 
Modern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better StrongerModern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
 
Data governance with Unity Catalog Presentation
Data governance with Unity Catalog PresentationData governance with Unity Catalog Presentation
Data governance with Unity Catalog Presentation
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
 
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platforms
 
[Webinar] SpiraTest - Setting New Standards in Quality Assurance
[Webinar] SpiraTest - Setting New Standards in Quality Assurance[Webinar] SpiraTest - Setting New Standards in Quality Assurance
[Webinar] SpiraTest - Setting New Standards in Quality Assurance
 
Assure Ecommerce and Retail Operations Uptime with ThousandEyes
Assure Ecommerce and Retail Operations Uptime with ThousandEyesAssure Ecommerce and Retail Operations Uptime with ThousandEyes
Assure Ecommerce and Retail Operations Uptime with ThousandEyes
 
Rise of the Machines: Known As Drones...
Rise of the Machines: Known As Drones...Rise of the Machines: Known As Drones...
Rise of the Machines: Known As Drones...
 
Take control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteTake control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test Suite
 
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
 
Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...
Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...
Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...
 

Featured

PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024Neil Kimberley
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)contently
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024Albert Qian
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsKurio // The Social Media Age(ncy)
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Search Engine Journal
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summarySpeakerHub
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next Tessa Mero
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentLily Ray
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best PracticesVit Horky
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project managementMindGenius
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...RachelPearson36
 
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Applitools
 
12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at WorkGetSmarter
 
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...DevGAMM Conference
 

Featured (20)

Skeleton Culture Code
Skeleton Culture CodeSkeleton Culture Code
Skeleton Culture Code
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search Intent
 
How to have difficult conversations
How to have difficult conversations How to have difficult conversations
How to have difficult conversations
 
Introduction to Data Science
Introduction to Data ScienceIntroduction to Data Science
Introduction to Data Science
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best Practices
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project management
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
 
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
 
12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work
 
ChatGPT webinar slides
ChatGPT webinar slidesChatGPT webinar slides
ChatGPT webinar slides
 
More than Just Lines on a Map: Best Practices for U.S Bike Routes
More than Just Lines on a Map: Best Practices for U.S Bike RoutesMore than Just Lines on a Map: Best Practices for U.S Bike Routes
More than Just Lines on a Map: Best Practices for U.S Bike Routes
 
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...
 

Paweł Szulc - Trip with monads at Scala in the City

  • 1. Trip with monads © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 1
  • 2. Why should I write FP code? 1. Modularity 2. Testability 3. Performance 4. Maintainability © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 2
  • 3. FP Building Blocks © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 3
  • 4. FP Building Blocks case class EitherT[F[_], A, B]( run: F[A / B] ) © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 4
  • 5. FP Building Blocks case class EitherT[F[_], A, B]( run: F[A / B] ) A / B © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 5
  • 6. FP Building Blocks case class EitherT[F[_], A, B]( run: F[A / B] ) A __/ B © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 6
  • 7. FP Building Blocks case class EitherT[F[_], A, B]( run: F[A / B] ) A _( )_/ B © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 7
  • 8. FP Building Blocks case class EitherT[F[_], A, B]( run: F[A / B] ) A ¯_( )_/¯ B © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 8
  • 9. FP Building Blocks case class Reader[A, B]( run: A => B ) © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 9
  • 10. FP Building Blocks case class Kleisli[M[_], A, B]( run: A => M[B] ) © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 10
  • 11. FP Building Blocks case class Kleisli[M[_], A, B]( run: A => M[B] ) type ReaderT[M[_], A, B] = Kleisli[M, A, B] © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 11
  • 12. FP Building Blocks case class Kleisli[M[_], A, B]( run: A => M[B] ) type ReaderT[M[_], A, B] = Kleisli[M, A, B] type Reader[A, B] = Kleisli[Id, A, B] © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 12
  • 13. FP Building Blocks case class Kleisli[M[_], A, B]( run: A => M[B] ) type ReaderT[M[_], A, B] = Kleisli[M, A, B] type Reader[A, B] = Kleisli[Id, A, B] type Id[A] = A © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 13
  • 14. FP Building Blocks case class State[S, A]( run: S => (S, A) ) © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 14
  • 15. FP Building Blocks case class StateT[M[_], S, A]( run: S => M[(S, A)] ) © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 15
  • 16. FP Building Blocks case class StateT[M[_], S, A]( run: S => M[(S, A)] ) type State[A, B] = StateT[Id, A, B] © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 16
  • 17. FP Building Blocks case class EitherT[F[_], A, B](run: F[A / B]) case class Kleisli[M[_], A, B](run: A => M[B]) type ReaderT[M[_], A, B] = Kleisli[M, A, B] type Reader[A, B] = Kleisli[Id, A, B] case class StateT[M[_], S, A](run: S => M[(S, A)]) type State[A, B] = StateT[Id, A, B] © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 17
  • 18. And now what? © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 18
  • 19. Learning Functional Programming © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 19
  • 20. Let's write FP application! © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 20
  • 21. The App > . © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 21
  • 22. The App > run . © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 22
  • 23. The App > run Using weather service at http://our-super-weather-forcast-app.com What is the next city? . © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 23
  • 24. The App > run Using weather service at http://our-super-weather-forcast-app.com What is the next city? Wroclaw . © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 24
  • 25. The App > run Using weather service at http://our-super-weather-forcast-app.com What is the next city? Wroclaw Forcast for City(Wroclaw) is Temperature(28, Celcius) Hottest city found so far is (City(Wroclaw), Temperature(28, Celcius)) What is the next city? . © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 25
  • 26. The App > run Using weather service at http://our-super-weather-forcast-app.com What is the next city? Wroclaw Forcast for City(Wroclaw) is Temperature(28, Celcius) Hottest city found so far is (City(Wroclaw), Temperature(28, Celcius)) What is the next city? London . © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 26
  • 27. The App > run Using weather service at http://our-super-weather-forcast-app.com What is the next city? Wroclaw Forcast for City(Wroclaw) is Temperature(28, Celcius) Hottest city found so far is (City(Wroclaw), Temperature(28, Celcius)) What is the next city? London Forcast for City(London) is Temperature(34, Celcius) Hottest city found so far is (City(London), Temperature(34, Celcius)) What is the next city? . © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 27
  • 28. The App > run Using weather service at http://our-super-weather-forcast-app.com What is the next city? Wroclaw Forcast for City(Wroclaw) is Temperature(28, Celcius) Hottest city found so far is (City(Wroclaw), Temperature(28, Celcius)) What is the next city? London Forcast for City(London) is Temperature(34, Celcius) Hottest city found so far is (City(London), Temperature(34, Celcius)) What is the next city? kjsdkjssd . © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 28
  • 29. The App > run Using weather service at http://our-super-weather-forcast-app.com What is the next city? Wroclaw Forcast for City(Wroclaw) is Temperature(28, Celcius) Hottest city found so far is (City(Wroclaw), Temperature(28, Celcius)) What is the next city? London Forcast for City(London) is Temperature(34, Celcius) Hottest city found so far is (City(London), Temperature(34, Celcius)) What is the next city? kjsdkjssd Encountered an error: UknownCity(kjsdkjssd) > © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 29
  • 30. Domain sealed trait TempUnit case object Celcius extends TempUnit case object Fahren extends TempUnit © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 30
  • 31. Domain case class Temperature( value: Int, unit: TempUnit = Celcius ) case class Forcast(temperature: Temperature) case class City(name: String) © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 31
  • 32. Third-party class WeatherClient(host: String, port: Int) { def forcast(city: City): Forcast = city match { case City("Wroclaw") => Forcast(Temperature(28)) case City("London") => Forcast(Temperature(34)) } } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 32
  • 33. Helper classes case class Config(host: String, port: Int) sealed trait Error case class UnknownCity(city: String) extends Error © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 33
  • 34. Helper classes type Requests = Map[City, Forcast] object Requests { def empty: Requests = Map.empty[City, Forcast] def hottest(requests: Requests): (City, Forcast) = ... } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 34
  • 35. Imports import scalaz._, Scalaz._ import monix.eval.Task import monix.execution.Scheduler import shims._ © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 35
  • 36. So what do we need? 1. Host & port 2. Input / output 3. City by name (errors) 4. Forcast from third-party 5. Hottest city © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 36
  • 37. Host & port def host: Reader[Config, String] = Reader(_.host) def port: Reader[Config, Int] = Reader(_.port) © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 37
  • 38. Input / output def printLn(line: String): Task[Unit] = Task.delay(println(line)) def readLn: Task[String] = Task.delay(scala.io.StdIn.readLine) © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 38
  • 39. City by name def cityByName(cityName: String): Error / City = cityName match { case "Wroclaw" => /-(City(cityName)) case "London" => /-(City(cityName)) case _ => -/(UnknownCity(cityName)) } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 39
  • 40. Weather forcast def weather(city: City)( host: String, port: Int ): Task[Forcast] = Task.delay { new WeatherClient(host, port).forcast(city) } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 40
  • 41. Hottest city def hottestCity: State[(City, Temperature)] = State { reqs => val temper = Requests.hottest(reqs) .map(f => f.temperature) (reqs, temper) } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 41
  • 42. Ask city def askCity: Task[String] = for { _ <- printLn("What is the next city?") cityName <- readLn } yield cityName © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 42
  • 43. def fetchForcast(city: City)(host: String, port: Int): StateReq[Forcast] = for { mForcast <- StateT { (reqs: Requests) => Task.delay ((reqs, reqs.get(city))) } forcast <- StateT { (reqs: Requests) => maybeForcast .cata(_.pure[Task], weather(city)(host, port)) .map (forcast => (reqs, forcast)) } _ <- StateT { (reqs: Requests) => Task.delay ((reqs + (city -> forcast), ())) } } yield forcast © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 43
  • 44. Final program def askFetchJudge = for { cityName <- askCity city <- cityByName(cityName) h <- host p <- port forcast <- fetchForcast(city)(host, port) _ <- printLn(s"Forcast for $city is ${forcast.temperature}") hottest <- hottestCity _ <- printLn(s"Hottest city found so far is $hottest") } yield () © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 44
  • 45. Final program def program = for { h <- host p <- port _ <- printLn(s"Using weather service at http://$h:$p n") _ <- askFetchJudge.forever } yield () © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 45
  • 46. WRONG! © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 46
  • 47. Reality def askFetchJudge: Effect[Unit] = for { cityName <- askCity.liftM[ErrorEitherT].liftM[ConfigReaderT].liftM[RequestsStateT] city <- EitherT .fromDisjunction[Task](cityByName(cityName)) .liftM[ConfigReaderT].liftM[RequestsStateT] h <- StateT[Effect1, Requests, String]((requests: Requests) => ReaderT[Effect0, Config, (Requests, String)]((config: Config) => EitherT(Task.delay { ((requests, host.run(config))).right[Error] }))) p <- StateT[Effect1, Requests, Int]((requests: Requests) => ReaderT[Effect0, Config, (Requests, Int)]((config: Config) => EitherT(Task.delay { ((requests, port.run(config))).right[Error] }))) forcast <- fetchForcast(city)(h, p) .mapK[Effect1, Forcast, Requests]( (t: Task[(Requests, Forcast)]) => ReaderT[EitherT[Task, Error, ?], Config, (Requests, Forcast)]( κ(EitherT[Task, Error, (Requests, Forcast)]( t.map(_.right[Error]))))) _ <- printLn(s"Forcast for $city is ${forcast.temperature}") .liftM[ErrorEitherT].liftM[ConfigReaderT].liftM[RequestsStateT] hottest <- hottestCity.mapK[Effect1, (City, Temperature), Requests]( (t: Task[(Requests, (City, Temperature))]) => ReaderT[EitherT[Task, Error, ?], Config, (Requests, (City, Temperature))]( κ(EitherT[Task, Error, (Requests, (City, Temperature))]( t.map(_.right[Error]))))) _ <- printLn(s"Hottest city found so far is $hottest") .liftM[ErrorEitherT] .liftM[ConfigReaderT] .liftM[RequestsStateT] } yield () © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 47
  • 48. Reality def program: Effect[Unit] = for { h <- StateT[Effect1, Requests, String]((requests: Requests) => ReaderT[Effect0, Config, (Requests, String)]((config: Config) => EitherT(Task.delay { ((requests, host.run(config))).right[Error] }))) p <- StateT[Effect1, Requests, Int]((requests: Requests) => ReaderT[Effect0, Config, (Requests, Int)]((config: Config) => EitherT(Task.delay { ((requests, port.run(config))).right[Error] }))) _ <- printLn(s"Using weather service at http://$h:$p n") .liftM[ErrorEitherT] .liftM[ConfigReaderT] .liftM[RequestsStateT] _ <- askFetchJudge.forever } yield () © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 48
  • 49. "The FP sounds rather like a mediæval monk, denying himself the pleasures of life in the hope that it will make him virtuous." © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 49
  • 51. Modularity ! Testability Performance Maintainability © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 51
  • 52. Modularity ! Testability ! Performance Maintainability © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 52
  • 53. Modularity ! Testability ! Performance ! Maintainability © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 53
  • 54. Modularity ! Testability ! Performance ! Maintainability ! © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 54
  • 55. def askFetchJudge: Effect[Unit] = .. © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 55
  • 56. def askFetchJudge: Effect[Unit] = .. type Effect0[A] = EitherT[Task, Error, A] type Effect1[A] = ReaderT[Effect0, Config, A] type Effect[A] = StateT[Effect1, Requests, A] © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 56
  • 57. def main(args: Array[String]): Unit = { val config = Config("localhost", 8080) val requests = Requests.empty } . © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 57
  • 58. def main(args: Array[String]): Unit = { val config = Config("localhost", 8080) val requests = Requests.empty val app: Effect[Unit] = program implicit val io: SchedulerService = Scheduler.io("io-scheduler") } . © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 58
  • 59. def main(args: Array[String]): Unit = { val config = Config("localhost", 8080) val requests = Requests.empty val app: Effect[Unit] = program implicit val io: SchedulerService = Scheduler.io("io-scheduler") app.run(requests).run(config).run } . © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 59
  • 60. def main(args: Array[String]): Unit = { val config = Config("localhost", 8080) val requests = Requests.empty val app: Effect[Unit] = program implicit val io: SchedulerService = Scheduler.io("io-scheduler") app.run(requests).run(config).run >>= { case -/(error) => printLn(s"Encountered an error:" + s"${error.shows}") case /-(_) => ().pure[Task] } } . © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 60
  • 61. def main(args: Array[String]): Unit = { val config = Config("localhost", 8080) val requests = Requests.empty val app: Effect[Unit] = program implicit val io: SchedulerService = Scheduler.io("io-scheduler") (app.run(requests).run(config).run >>= { case -/(error) => printLn(s"Encountered an error:" + s"${error.shows}") case /-(_) => ().pure[Task] }).unsafeRunSync } . © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 61
  • 62. Can we do better? © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 62
  • 63. So what do we need? 1. Host & port 2. Input / output 3. City by name (errors) 4. Forcast from third-party 5. Hottest city © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 63
  • 64. Host & port def host: Reader[Config, String] = Reader(_.host) def port: Reader[Config, Int] = Reader(_.port) © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 64
  • 65. Host & port def host[F[_]]: F[String] = ? def port[F[_]]: F[Int] = ? © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 65
  • 66. trait ApplicativeAsk[F[_], E] { val applicative: Applicative[F] def ask: F[E] def reader[A](f: E => A): F[A] } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 66
  • 67. trait ApplicativeAsk[F[_], E] { val applicative: Applicative[F] def ask: F[E] def reader[A](f: E => A): F[A] } type ConfigAsk[F[_]] = ApplicativeAsk[F, Config] © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 67
  • 68. def host[F[_]]: F[String] = ? def port[F[_]]: F[Int] = ? © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 68
  • 69. def host[F[_]: ConfigAsk]: F[String] = ConfigAsk[F].reader(_.host) def port[F[_]: ConfigAsk]: F[Int] = ConfigAsk[F].reader(_.port) © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 69
  • 70. def host[F[_]: ConfigAsk]: F[String] = ConfigAsk[F].reader(_.host) def port[F[_]: ConfigAsk]: F[Int] = ConfigAsk[F].reader(_.port) trait ApplicativeAsk[F[_], E] { val applicative: Applicative[F] def ask: F[E] def reader[A](f: E => A): F[A] } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 70
  • 71. City by name def cityByName(cityName: String): Error / City = cityName match { case "Wroclaw" => /-(City(cityName)) case "London" => /-(City(cityName)) case _ => -/(UnknownCity(cityName)) } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 71
  • 72. City by name def cityByName[F[_]]( cityName: String ): F[City] = } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 72
  • 73. City by name def cityByName[F[_]:Applicative]( cityName: String ): F[City] = cityName match { case "Wroclaw" => City(cityName).pure[F] case "London" => City(cityName).pure[F] case _ => ??? } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 73
  • 74. trait ApplicativeError[F[_], E] { val applicative: Applicative[F] def raiseError[A](e: E): F[A] } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 74
  • 75. trait ApplicativeError[F[_], E] { val applicative: Applicative[F] def raiseError[A](e: E): F[A] } type ErrorHandler[F[_]] = ApplicativeError[F, Error] © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 75
  • 76. City by name def cityByName[F[_]:Applicative]( cityName: String ): F[City] = cityName match { case "Wroclaw" => City(cityName).pure[F] case "London" => City(cityName).pure[F] case _ => ??? } . © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 76
  • 77. City by name def cityByName[F[_]: Applicative: ErrorHandler]( cityName: String ): F[City] = cityName match { case "Wroclaw" => City(cityName).pure[F] case "London" => City(cityName).pure[F] case _ => ErrorHandler[F].raiseError(UnknownCity(cityName)) } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 77
  • 78. Hottest city def hottestCity: State[(City, Temperature)] = State { reqs => val temper = Requests.hottest(reqs) .map(f => f.temperature) (reqs, temper) } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 78
  • 79. Hottest city def hottestCity[F[_]]: F[(City, Temperature)]= ? © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 79
  • 80. trait MonadState[F[_], S] { val monad: Monad[F] def get: F[S] def set(s: S): F[Unit] def inspect[A](f: S => A): F[A] def modify(f: S => S): F[Unit] } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 80
  • 81. trait MonadState[F[_], S] { val monad: Monad[F] def get: F[S] def set(s: S): F[Unit] def inspect[A](f: S => A): F[A] def modify(f: S => S): F[Unit] } type RequestsState[F[_]] = MonadState[F, Requests] © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 81
  • 82. Hottest city def hottestCity[F[_]]: F[(City, Temperature)]= ? © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 82
  • 83. Hottest city def hottestCity[ F[_]: RequestsState ]: F[(City, Temperature)] = © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 83
  • 84. Hottest city def hottestCity[ F[_]: RequestsState ]: F[(City, Temperature)] = RequestsState[F].inspect(reqs => Requests.hottest(reqs).map(_.temperature) ) © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 84
  • 85. Hottest city def hottestCity[ F[_]: RequestsState ]: F[(City, Temperature)] = RequestsState[F].inspect(reqs => Requests.hottest(reqs).map(_.temperature) ) trait MonadState[F[_], S] { // (...) def inspect[A](f: S => A): F[A] } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 85
  • 86. MTL© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 86
  • 87. Input / output def printLn(line: String): Task[Unit] = Task.delay(println(line)) def readLn: Task[String] = Task.delay(scala.io.StdIn.readLine) © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 87
  • 88. Input / output trait Console[F[_]] { def readLine: F[String] def printLn(line: String): F[Unit] } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 88
  • 89. Ask City def askCity[F[_]: Console: Monad]: F[String] = for { _ <- Console[F].printLn("What is the next city?") cityName <- Console[F].readLine } yield cityName © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 89
  • 90. Weather forcast def weather(city: City)( host: String, port: Int ): Task[Forcast] = Task.delay { new WeatherClient(host, port).forcast(city) } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 90
  • 91. Weather forcast trait Weather[F[_]] { def forcast(city: City): F[Forcast] } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 91
  • 92. Fetch forcast def fetchForcast[F[_]: Weather: RequestsState: Monad](city: City): F[Forcast] = for { maybeForcast <- RequestsState[F].inspect(_.get(city)) forcast <- maybeForcast.cata( _.pure[F], Weather[F].forcast(city) ) _ <- RequestsState[F].modify(_ + (city -> forcast)) } yield forcast © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 92
  • 93. Tagless final © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 93
  • 94. Final program def askFetchJudge[F[_]: Console: Weather : RequestsState: ErrorHandler:Monad]: F[Unit] = for { cityName <- askCity[F] city <- cityByName[F](cityName) forcast <- fetchForcast[F](city) _ <- Console[F].printLn(s"Forcast for $city is ${forcast.temperature}") hottest <- hottestCity[F] _ <- Console[F].printLn(s"Hottest city found so far is $hottest") } yield () © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 94
  • 95. Final program def program[F[_]: ConfigAsk: Console: Weather: RequestsState:ErrorHandler:Monad]: F[Unit] = for { h <- host[F] p <- port[F] _ <- Console[F].printLn(s"Using weather service at http://$h:$p") _ <- askFetchJudge[F].forever } yield () © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 95
  • 96. def main(args: Array[String]): Unit = { val config = Config("localhost", 8080) val requests = Requests.empty type Effect[A] = ??? val app: Effect[Unit] = program[Effect] } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 96
  • 97. def main(args: Array[String]): Unit = { val config = Config("localhost", 8080) val requests = Requests.empty type Effect[A] = Task[A] val app: Effect[Unit] = program[Effect] } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 97
  • 98. def main(args: Array[String]): Unit = { val config = Config("localhost", 8080) val requests = Requests.empty type Effect[A] = Task[A] implicit val weather = ??? val app: Effect[Unit] = program[Effect] } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 98
  • 99. object Weather { def monixWeather(config: Config): Weather[Task] = new Weather[Task] { val client: WeatherClient = new WeatherClient(config.host, config.port) def forcast(city: City): Task[Forcast] = Task.delay { client.forcast(city) } } } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 99
  • 100. def main(args: Array[String]): Unit = { val config = Config("localhost", 8080) val requests = Requests.empty type Effect[A] = Task[A] implicit val weather = ??? val app: Effect[Unit] = program[Effect] } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 100
  • 101. def main(args: Array[String]): Unit = { val config = Config("localhost", 8080) val requests = Requests.empty type Effect[A] = Task[A] implicit val weather = Weather.monixWeather(config) val app: Effect[Unit] = program[Effect] } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 101
  • 102. def main(args: Array[String]): Unit = { val config = Config("localhost", 8080) val requests = Requests.empty type Effect[A] = Task[A] implicit val weather = Weather.monixWeather(config) implicit val console = ??? val app: Effect[Unit] = program[Effect] } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 102
  • 103. object Console { val monixConsole: Console[Task] = new Console[Task] { def readLine: Task[String] = Task.delay(scala.io.StdIn.readLine) def printLn(line: String): Task[Unit] = Task.delay(println(line)) } } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 103
  • 104. def main(args: Array[String]): Unit = { val config = Config("localhost", 8080) val requests = Requests.empty type Effect[A] = Task[A] implicit val weather = Weather.monixWeather(config) implicit val console = ??? val app: Effect[Unit] = program[Effect] } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 104
  • 105. def main(args: Array[String]): Unit = { val config = Config("localhost", 8080) val requests = Requests.empty type Effect[A] = Task[A] implicit val weather = Weather.monixWeather(config) implicit val console = Console.monixConsole val app: Effect[Unit] = program[Effect] } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 105
  • 106. def main(args: Array[String]): Unit = { val config = Config("localhost", 8080) val requests = Requests.empty type Effect[A] = StateT[Task, Requests, A] implicit val weather = Weather.monixWeather(config) implicit val console = Console.monixConsole val app: Effect[Unit] = program[Effect] } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 106
  • 107. def main(args: Array[String]): Unit = { val config = Config("localhost", 8080) val requests = Requests.empty type Effect1[A] = ReaderT[Task, Config, A] type Effect[A] = StateT[Effect1, Requests, A] implicit val weather = Weather.monixWeather(config) implicit val console = Console.monixConsole val app: Effect[Unit] = program[Effect] } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 107
  • 108. def main(args: Array[String]): Unit = { val config = Config("localhost", 8080) val requests = Requests.empty type Effect0[A] = EitherT[Task, Error, A] type Effect1[A] = ReaderT[Effect0, Config, A] type Effect[A] = StateT[Effect1, Requests, A] implicit val weather = Weather.monixWeather(config) implicit val console = Console.monixConsole val app: Effect[Unit] = program[Effect] } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 108
  • 109. def main(args: Array[String]): Unit = { val config = Config("localhost", 8080) val requests = Requests.empty type Effect0[A] = EitherT[Task, Error, A] type Effect1[A] = ReaderT[Effect0, Config, A] type Effect[A] = StateT[Effect1, Requests, A] implicit val weather = Weather.monixWeather(config) implicit val console = Console.monixConsole val app: Effect[Unit] = program[Effect] (app.run(requests).run(config).run >>= { case -/(error) => console.printLn(s"Encountered an error: ${error.shows}") case /-(_) => ().pure[Task] }).unsafeRunSync } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 109
  • 111. Modularity ✅ Testability Performance Maintainability © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 111
  • 112. def askFetchJudge[F[_]: Console: Weather : RequestsState: ErrorHandler:Monad]: F[Unit] = for { cityName <- askCity[F] city <- cityByName[F](cityName) forcast <- fetchForcast[F](city) _ <- Console[F].printLn(s"Forcast for $city is ${forcast.temperature}") hottest <- hottestCity[F] _ <- Console[F].printLn(s"Hottest city found so far is $hottest") } yield () © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 112
  • 113. Modularity ✅ Testability Performance Maintainability © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 113
  • 114. Modularity ✅ Testability ✅ Performance Maintainability © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 114
  • 115. Modularity ✅ Testability ✅ Performance " Maintainability © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 115
  • 116. Modularity ✅ Testability ✅ Performance " Maintainability ✅ © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 116
  • 117. Can we do better? © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 117
  • 118. def main(args: Array[String]): Unit = { val config = Config("localhost", 8080) val requests = Requests.empty type Effect0[A] = EitherT[Task, Error, A] type Effect1[A] = ReaderT[Effect0, Config, A] type Effect[A] = StateT[Effect1, Requests, A] implicit val weather = Weather.monixWeather(config) implicit val console = Console.monixConsole } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 118
  • 119. private[twm] class AtomicMonadState[S](atomic: Atomic[S]) extends MonadState[Task, S] { val monad: Monad[Task] = Monad[Task] def get: Task[S] = Task.delay(atomic.get) def set(s: S): Task[Unit] = Task.delay(atomic.set(s)) def inspect[A](f: S => A): Task[A] = Task.delay(f(atomic.get)) def modify(f: S => S): Task[Unit] = Task.delay(atomic.transform(f)) } object AtomicMonadState { def create[S <: AnyRef](s: S): AtomicMonadState[S] = new AtomicMonadState(AtomicAny[S](s)) } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 119
  • 120. def main(args: Array[String]): Unit = { val config = Config("localhost", 8080) val requests = Requests.empty type Effect0[A] = EitherT[Task, Error, A] type Effect1[A] = ReaderT[Effect0, Config, A] type Effect[A] = StateT[Effect1, Requests, A] implicit val weather = Weather.monixWeather(config) implicit val console = Console.monixConsole } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 120
  • 121. def main(args: Array[String]): Unit = { val config = Config("localhost", 8080) val requests = Requests.empty type Effect0[A] = EitherT[Task, Error, A] type Effect[A] = ReaderT[Effect0, Config, A] implicit val weather = Weather.monixWeather(config) implicit val console = Console.monixConsole implicit val requestsState = AtomicMonadState.create(Requests.empty) } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 121
  • 122. object ApplicativeAsk { def constant[ F[_]:Applicative, E ](e: E): ApplicativeAsk[F, E] = new DefaultApplicativeAsk[F, E] { val applicative: Applicative[F] = Applicative[F] def ask: F[E] = applicative.point(e) } } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 122
  • 123. def main(args: Array[String]): Unit = { val config = Config("localhost", 8080) val requests = Requests.empty type Effect0[A] = EitherT[Task, Error, A] type Effect[A] = ReaderT[Effect0, Config, A] implicit val weather = Weather.monixWeather(config) implicit val console = Console.monixConsole implicit val requestsState = AtomicMonadState.create(Requests.empty) } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 123
  • 124. def main(args: Array[String]): Unit = { val config = Config("localhost", 8080) val requests = Requests.empty type Effect[A] = EitherT[Task, Error, A] implicit val weather = Weather.monixWeather(config) implicit val console = Console.monixConsole implicit val requestsState = AtomicMonadState.create(Requests.empty) implicit val configAsk = ApplicativeAsk.constant[Task, Config](config) } © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 124
  • 125. Modularity ✅ Testability ✅ Performance " Maintainability ✅ © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 125
  • 126. Modularity ✅ Testability ✅ Performance ✅ * Maintainability ✅ © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 126
  • 127. Can we do better? © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 127
  • 128. type Effect[A] = EitherT[Task, Error, A] Last frontier © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 128
  • 129. Why? © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 129
  • 130. Why? 1. Performance 2. Two channels error handling © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 130
  • 131. Introducing IO[E, A] © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 131
  • 133. Modularity ✅ Testability ✅ Performance ✅ * Maintainability ✅ © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 133
  • 134. Modularity ✅ Testability ✅ Performance ✅ Maintainability ✅ © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 134
  • 135. Announcements © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 135
  • 136. Announcements 1. https://github.com/scalaz/scalaz-mtl 2. http://rabbitonweb.com/ © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 136
  • 137. Thank you very much Pawel Szulc @rabbitonweb © Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 137