Weitere ähnliche Inhalte
Mehr von Pawel Szulc (20)
Kürzlich hochgeladen (20)
Trip with monads
- 2. Why should I write FP code?
1. Modularity
2. Testability
3. Performance
4. Maintainability
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 2
- 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
- 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
- 20. Let's write FP application!
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 20
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 137. Thank you very much
Pawel Szulc
@rabbitonweb
© Pawel Szulc, @rabbitonweb, paul.szulc@gmail.com 137