SlideShare a Scribd company logo
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?
Gdansk
.
© 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?
Gdansk
Forcast for City(Gdansk) is Temperature(34, Celcius)
Hottest city found so far is (City(Gdansk), 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?
Gdansk
Forcast for City(Gdansk) is Temperature(34, Celcius)
Hottest city found so far is (City(Gdansk), 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?
Gdansk
Forcast for City(Gdansk) is Temperature(34, Celcius)
Hottest city found so far is (City(Gdansk), 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("Gdansk") =>
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 "Gdansk" =>
/-(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 "Gdansk" => /-(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 "Gdansk" => 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 "Gdansk" => 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 "Gdansk" => 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

More from Pawel Szulc

Impossibility
ImpossibilityImpossibility
Impossibility
Pawel Szulc
 
Maintainable Software Architecture in Haskell (with Polysemy)
Maintainable Software Architecture in Haskell (with Polysemy)Maintainable Software Architecture in Haskell (with Polysemy)
Maintainable Software Architecture in Haskell (with Polysemy)
Pawel Szulc
 
Painless Haskell
Painless HaskellPainless Haskell
Painless Haskell
Pawel Szulc
 
Illogical engineers
Illogical engineersIllogical engineers
Illogical engineers
Pawel Szulc
 
RChain - Understanding Distributed Calculi
RChain - Understanding Distributed CalculiRChain - Understanding Distributed Calculi
RChain - Understanding Distributed Calculi
Pawel Szulc
 
Illogical engineers
Illogical engineersIllogical engineers
Illogical engineers
Pawel Szulc
 
Understanding distributed calculi in Haskell
Understanding distributed calculi in HaskellUnderstanding distributed calculi in Haskell
Understanding distributed calculi in Haskell
Pawel Szulc
 
Software engineering the genesis
Software engineering  the genesisSoftware engineering  the genesis
Software engineering the genesis
Pawel Szulc
 
Make your programs Free
Make your programs FreeMake your programs Free
Make your programs Free
Pawel Szulc
 
Going bananas with recursion schemes for fixed point data types
Going bananas with recursion schemes for fixed point data typesGoing bananas with recursion schemes for fixed point data types
Going bananas with recursion schemes for fixed point data types
Pawel Szulc
 
“Going bananas with recursion schemes for fixed point data types”
“Going bananas with recursion schemes for fixed point data types”“Going bananas with recursion schemes for fixed point data types”
“Going bananas with recursion schemes for fixed point data types”
Pawel Szulc
 
Writing your own RDD for fun and profit
Writing your own RDD for fun and profitWriting your own RDD for fun and profit
Writing your own RDD for fun and profit
Pawel Szulc
 
The cats toolbox a quick tour of some basic typeclasses
The cats toolbox  a quick tour of some basic typeclassesThe cats toolbox  a quick tour of some basic typeclasses
The cats toolbox a quick tour of some basic typeclasses
Pawel Szulc
 
Introduction to type classes
Introduction to type classesIntroduction to type classes
Introduction to type classes
Pawel Szulc
 
Functional Programming & Event Sourcing - a pair made in heaven
Functional Programming & Event Sourcing - a pair made in heavenFunctional Programming & Event Sourcing - a pair made in heaven
Functional Programming & Event Sourcing - a pair made in heaven
Pawel Szulc
 
Apache spark workshop
Apache spark workshopApache spark workshop
Apache spark workshop
Pawel Szulc
 
Introduction to type classes in 30 min
Introduction to type classes in 30 minIntroduction to type classes in 30 min
Introduction to type classes in 30 min
Pawel Szulc
 
Real world gobbledygook
Real world gobbledygookReal world gobbledygook
Real world gobbledygook
Pawel Szulc
 
Apache spark when things go wrong
Apache spark   when things go wrongApache spark   when things go wrong
Apache spark when things go wrong
Pawel Szulc
 
Category theory is general abolute nonsens
Category theory is general abolute nonsensCategory theory is general abolute nonsens
Category theory is general abolute nonsens
Pawel Szulc
 

More from Pawel Szulc (20)

Impossibility
ImpossibilityImpossibility
Impossibility
 
Maintainable Software Architecture in Haskell (with Polysemy)
Maintainable Software Architecture in Haskell (with Polysemy)Maintainable Software Architecture in Haskell (with Polysemy)
Maintainable Software Architecture in Haskell (with Polysemy)
 
Painless Haskell
Painless HaskellPainless Haskell
Painless Haskell
 
Illogical engineers
Illogical engineersIllogical engineers
Illogical engineers
 
RChain - Understanding Distributed Calculi
RChain - Understanding Distributed CalculiRChain - Understanding Distributed Calculi
RChain - Understanding Distributed Calculi
 
Illogical engineers
Illogical engineersIllogical engineers
Illogical engineers
 
Understanding distributed calculi in Haskell
Understanding distributed calculi in HaskellUnderstanding distributed calculi in Haskell
Understanding distributed calculi in Haskell
 
Software engineering the genesis
Software engineering  the genesisSoftware engineering  the genesis
Software engineering the genesis
 
Make your programs Free
Make your programs FreeMake your programs Free
Make your programs Free
 
Going bananas with recursion schemes for fixed point data types
Going bananas with recursion schemes for fixed point data typesGoing bananas with recursion schemes for fixed point data types
Going bananas with recursion schemes for fixed point data types
 
“Going bananas with recursion schemes for fixed point data types”
“Going bananas with recursion schemes for fixed point data types”“Going bananas with recursion schemes for fixed point data types”
“Going bananas with recursion schemes for fixed point data types”
 
Writing your own RDD for fun and profit
Writing your own RDD for fun and profitWriting your own RDD for fun and profit
Writing your own RDD for fun and profit
 
The cats toolbox a quick tour of some basic typeclasses
The cats toolbox  a quick tour of some basic typeclassesThe cats toolbox  a quick tour of some basic typeclasses
The cats toolbox a quick tour of some basic typeclasses
 
Introduction to type classes
Introduction to type classesIntroduction to type classes
Introduction to type classes
 
Functional Programming & Event Sourcing - a pair made in heaven
Functional Programming & Event Sourcing - a pair made in heavenFunctional Programming & Event Sourcing - a pair made in heaven
Functional Programming & Event Sourcing - a pair made in heaven
 
Apache spark workshop
Apache spark workshopApache spark workshop
Apache spark workshop
 
Introduction to type classes in 30 min
Introduction to type classes in 30 minIntroduction to type classes in 30 min
Introduction to type classes in 30 min
 
Real world gobbledygook
Real world gobbledygookReal world gobbledygook
Real world gobbledygook
 
Apache spark when things go wrong
Apache spark   when things go wrongApache spark   when things go wrong
Apache spark when things go wrong
 
Category theory is general abolute nonsens
Category theory is general abolute nonsensCategory theory is general abolute nonsens
Category theory is general abolute nonsens
 

Recently uploaded

GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...
GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...
GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...
Neo4j
 
Pushing the limits of ePRTC: 100ns holdover for 100 days
Pushing the limits of ePRTC: 100ns holdover for 100 daysPushing the limits of ePRTC: 100ns holdover for 100 days
Pushing the limits of ePRTC: 100ns holdover for 100 days
Adtran
 
Generative AI Deep Dive: Advancing from Proof of Concept to Production
Generative AI Deep Dive: Advancing from Proof of Concept to ProductionGenerative AI Deep Dive: Advancing from Proof of Concept to Production
Generative AI Deep Dive: Advancing from Proof of Concept to Production
Aggregage
 
20240609 QFM020 Irresponsible AI Reading List May 2024
20240609 QFM020 Irresponsible AI Reading List May 202420240609 QFM020 Irresponsible AI Reading List May 2024
20240609 QFM020 Irresponsible AI Reading List May 2024
Matthew Sinclair
 
UiPath Test Automation using UiPath Test Suite series, part 6
UiPath Test Automation using UiPath Test Suite series, part 6UiPath Test Automation using UiPath Test Suite series, part 6
UiPath Test Automation using UiPath Test Suite series, part 6
DianaGray10
 
Secstrike : Reverse Engineering & Pwnable tools for CTF.pptx
Secstrike : Reverse Engineering & Pwnable tools for CTF.pptxSecstrike : Reverse Engineering & Pwnable tools for CTF.pptx
Secstrike : Reverse Engineering & Pwnable tools for CTF.pptx
nkrafacyberclub
 
Free Complete Python - A step towards Data Science
Free Complete Python - A step towards Data ScienceFree Complete Python - A step towards Data Science
Free Complete Python - A step towards Data Science
RinaMondal9
 
The Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and SalesThe Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and Sales
Laura Byrne
 
Removing Uninteresting Bytes in Software Fuzzing
Removing Uninteresting Bytes in Software FuzzingRemoving Uninteresting Bytes in Software Fuzzing
Removing Uninteresting Bytes in Software Fuzzing
Aftab Hussain
 
Monitoring Java Application Security with JDK Tools and JFR Events
Monitoring Java Application Security with JDK Tools and JFR EventsMonitoring Java Application Security with JDK Tools and JFR Events
Monitoring Java Application Security with JDK Tools and JFR Events
Ana-Maria Mihalceanu
 
20240607 QFM018 Elixir Reading List May 2024
20240607 QFM018 Elixir Reading List May 202420240607 QFM018 Elixir Reading List May 2024
20240607 QFM018 Elixir Reading List May 2024
Matthew Sinclair
 
GraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge GraphGraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge Graph
Guy Korland
 
Epistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI supportEpistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI support
Alan Dix
 
Climate Impact of Software Testing at Nordic Testing Days
Climate Impact of Software Testing at Nordic Testing DaysClimate Impact of Software Testing at Nordic Testing Days
Climate Impact of Software Testing at Nordic Testing Days
Kari Kakkonen
 
Video Streaming: Then, Now, and in the Future
Video Streaming: Then, Now, and in the FutureVideo Streaming: Then, Now, and in the Future
Video Streaming: Then, Now, and in the Future
Alpen-Adria-Universität
 
Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !
KatiaHIMEUR1
 
Elizabeth Buie - Older adults: Are we really designing for our future selves?
Elizabeth Buie - Older adults: Are we really designing for our future selves?Elizabeth Buie - Older adults: Are we really designing for our future selves?
Elizabeth Buie - Older adults: Are we really designing for our future selves?
Nexer Digital
 
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdfFIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance
 
DevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA ConnectDevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA Connect
Kari Kakkonen
 
Artificial Intelligence for XMLDevelopment
Artificial Intelligence for XMLDevelopmentArtificial Intelligence for XMLDevelopment
Artificial Intelligence for XMLDevelopment
Octavian Nadolu
 

Recently uploaded (20)

GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...
GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...
GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...
 
Pushing the limits of ePRTC: 100ns holdover for 100 days
Pushing the limits of ePRTC: 100ns holdover for 100 daysPushing the limits of ePRTC: 100ns holdover for 100 days
Pushing the limits of ePRTC: 100ns holdover for 100 days
 
Generative AI Deep Dive: Advancing from Proof of Concept to Production
Generative AI Deep Dive: Advancing from Proof of Concept to ProductionGenerative AI Deep Dive: Advancing from Proof of Concept to Production
Generative AI Deep Dive: Advancing from Proof of Concept to Production
 
20240609 QFM020 Irresponsible AI Reading List May 2024
20240609 QFM020 Irresponsible AI Reading List May 202420240609 QFM020 Irresponsible AI Reading List May 2024
20240609 QFM020 Irresponsible AI Reading List May 2024
 
UiPath Test Automation using UiPath Test Suite series, part 6
UiPath Test Automation using UiPath Test Suite series, part 6UiPath Test Automation using UiPath Test Suite series, part 6
UiPath Test Automation using UiPath Test Suite series, part 6
 
Secstrike : Reverse Engineering & Pwnable tools for CTF.pptx
Secstrike : Reverse Engineering & Pwnable tools for CTF.pptxSecstrike : Reverse Engineering & Pwnable tools for CTF.pptx
Secstrike : Reverse Engineering & Pwnable tools for CTF.pptx
 
Free Complete Python - A step towards Data Science
Free Complete Python - A step towards Data ScienceFree Complete Python - A step towards Data Science
Free Complete Python - A step towards Data Science
 
The Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and SalesThe Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and Sales
 
Removing Uninteresting Bytes in Software Fuzzing
Removing Uninteresting Bytes in Software FuzzingRemoving Uninteresting Bytes in Software Fuzzing
Removing Uninteresting Bytes in Software Fuzzing
 
Monitoring Java Application Security with JDK Tools and JFR Events
Monitoring Java Application Security with JDK Tools and JFR EventsMonitoring Java Application Security with JDK Tools and JFR Events
Monitoring Java Application Security with JDK Tools and JFR Events
 
20240607 QFM018 Elixir Reading List May 2024
20240607 QFM018 Elixir Reading List May 202420240607 QFM018 Elixir Reading List May 2024
20240607 QFM018 Elixir Reading List May 2024
 
GraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge GraphGraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge Graph
 
Epistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI supportEpistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI support
 
Climate Impact of Software Testing at Nordic Testing Days
Climate Impact of Software Testing at Nordic Testing DaysClimate Impact of Software Testing at Nordic Testing Days
Climate Impact of Software Testing at Nordic Testing Days
 
Video Streaming: Then, Now, and in the Future
Video Streaming: Then, Now, and in the FutureVideo Streaming: Then, Now, and in the Future
Video Streaming: Then, Now, and in the Future
 
Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !
 
Elizabeth Buie - Older adults: Are we really designing for our future selves?
Elizabeth Buie - Older adults: Are we really designing for our future selves?Elizabeth Buie - Older adults: Are we really designing for our future selves?
Elizabeth Buie - Older adults: Are we really designing for our future selves?
 
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdfFIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
 
DevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA ConnectDevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA Connect
 
Artificial Intelligence for XMLDevelopment
Artificial Intelligence for XMLDevelopmentArtificial Intelligence for XMLDevelopment
Artificial Intelligence for XMLDevelopment
 

Trip with monads

  • 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? Gdansk . © 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? Gdansk Forcast for City(Gdansk) is Temperature(34, Celcius) Hottest city found so far is (City(Gdansk), 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? Gdansk Forcast for City(Gdansk) is Temperature(34, Celcius) Hottest city found so far is (City(Gdansk), 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? Gdansk Forcast for City(Gdansk) is Temperature(34, Celcius) Hottest city found so far is (City(Gdansk), 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("Gdansk") => 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 "Gdansk" => /-(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 "Gdansk" => /-(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 "Gdansk" => 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 "Gdansk" => 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 "Gdansk" => 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