SlideShare ist ein Scribd-Unternehmen logo
1 von 137
Downloaden Sie, um offline zu lesen
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

Weitere ähnliche Inhalte

Mehr von Pawel Szulc

Mehr von 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
 

Kürzlich hochgeladen

Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Safe Software
 
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Victor Rentea
 
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Victor Rentea
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Safe Software
 

Kürzlich hochgeladen (20)

[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf
 
CNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In PakistanCNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In Pakistan
 
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
 
Mcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Mcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot ModelMcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Mcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot Model
 
Introduction to use of FHIR Documents in ABDM
Introduction to use of FHIR Documents in ABDMIntroduction to use of FHIR Documents in ABDM
Introduction to use of FHIR Documents in ABDM
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ..."I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
 
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
 
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 AmsterdamDEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
 
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
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...
 
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
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
 
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
 
Introduction to Multilingual Retrieval Augmented Generation (RAG)
Introduction to Multilingual Retrieval Augmented Generation (RAG)Introduction to Multilingual Retrieval Augmented Generation (RAG)
Introduction to Multilingual Retrieval Augmented Generation (RAG)
 
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
 
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdfRising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
 

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