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

Recently uploaded (20)

The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdf
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...
Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...
Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 

Featured

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
Kurio // The Social Media Age(ncy)
 

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