Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Functional Web Services with Final Encoding

Functional Web Services with Final Encoding

From flatMap(Oslo) 2017

Markus Hauck

May 02, 2017
Tweet

More Decks by Markus Hauck

Other Decks in Programming

Transcript

  1. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s What are we gonna do tonight? Markus Hauck (codecentric AG) Functional Web Services slide 1
  2. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Markus Hauck (codecentric AG) Functional Web Services slide 2
  3. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s What are we going to do tonight • explore final encodings • write embedded DSLs • craft beautiful web services https://github.com/markus1189/runnersparadise Markus Hauck (codecentric AG) Functional Web Services slide 3
  4. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Before We Start: Embedded DSLs • embedded in general purpose language (host language) • separate description of program from execution • one of the core principles in FP Markus Hauck (codecentric AG) Functional Web Services slide 4
  5. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s A First DSL • let’s start with our first DSL • arithmetic operations: • numeric literals • addition: e1 + e2 • negation: -e 21 + 21 50 + -(6 + 2) -(-20 + -20 + -2) Markus Hauck (codecentric AG) Functional Web Services slide 5
  6. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Common Approach: Build AST sealed trait Exp case class Lit(x: Int) extends Exp case class Neg(e: Exp) extends Exp case class Add(e1: Exp, e2: Exp) extends Exp • writing a program (syntactic sugar omitted) // 50 + -(6 + 2) Add(Lit(50), Neg(Add(Lit(6), Lit(2)))) Markus Hauck (codecentric AG) Functional Web Services slide 6
  7. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Interpretation • we captured the program into a value • able to use different interpreters def calculate(p: Exp): Int = p match { case Lit(x) => x case Neg(e) => -calculate(e) case Add(e1,e2) => calculate(e1) + calculate(e2) } def pretty(p: Exp): String = ??? def async(srv: ExpSrv)(p: Exp): Future[Int] = ??? Markus Hauck (codecentric AG) Functional Web Services slide 7
  8. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Initial Encoding: Drawbacks • reify explicitly as AST ≈ initial encoding • pro: interpreters can be added easily • con: try to add multiplication as an addon • we don’t want to change Exp • old interpreters should work as before Markus Hauck (codecentric AG) Functional Web Services slide 8
  9. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Alternative • what if we scratch the whole “reify as AST” step? • leads to final encodings • • no datatype • instead: write type classes • instances = interpreter • let’s define another calculator Markus Hauck (codecentric AG) Functional Web Services slide 9
  10. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Final Encoding: Calculator sealed trait Exp[A] { def lit(x: Int): A def neg(e: A): A def add(e1: A, e2: A): A } • writing a program // 50 + -(6 + 2) def program[A](implicit E: Exp[A]): A = { import E._ // for demo add(lit(50), neg(add(lit(6), lit(2)))) } Markus Hauck (codecentric AG) Functional Web Services slide 10
  11. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Final Encoding: Interpreter • define interpreters as instances: implicit val calc: Exp[Int] = new Exp[Int] { def lit(x: Int): Int = x def neg(e: Int): Int = -e def add(e1: Int, e2: Int): Int = e1 + e2 } Markus Hauck (codecentric AG) Functional Web Services slide 11
  12. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Final Encoding: Interpreter implicit val pretty: Exp[String] = new Exp[String] { def lit(x: Int): String = x.shows def neg(e: String): String = "-" + e def add(e1: String, e2: String) = s"($e1 + $e2)" } Markus Hauck (codecentric AG) Functional Web Services slide 12
  13. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Final Encoding: Running • now that we have interpreters, we can run our program: scala> program[Int] res0: Int = 42 scala> program[String] res1: String = (50 + -(6 + 2)) • (please use newtypes for instances) Markus Hauck (codecentric AG) Functional Web Services slide 13
  14. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Final Encoding: Extension • the important part: let’s add multiplication • goal: change should not impact existing code Markus Hauck (codecentric AG) Functional Web Services slide 14
  15. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Final Encoding: Multiplication • add new typeclass • state required operations • note: independent of the others sealed trait ExpMul[A] { def mul(e1: A, e2: A): A } Markus Hauck (codecentric AG) Functional Web Services slide 15
  16. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Final Encoding: Using Multiplication def mulProgram[A]( implicit E1: Exp[A], E2: ExpMul[A] ): A = { import E1._, E2._ // 2 * (1 + (10 + 10)) mul(lit(2), add(lit(1), add(lit(10), lit(10)))) } Markus Hauck (codecentric AG) Functional Web Services slide 16
  17. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Final Encoding: Extend Interpreters • before we can run this, we need to add the instances implicit val calc: ExpMul[Int] = new ExpMul[Int] { def mul(e1: Int, e2: Int): Int = e1 * e2 } implicit val pretty: ExpMul[String] = new ExpMul[String] { def mul(e1: String, e2: String): String = s"($e1 * $e2)" } Markus Hauck (codecentric AG) Functional Web Services slide 17
  18. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Final Encoding: Execute Multiplication scala> mulProgram[Int] res0: Int = 42 scala> mulProgram[String] res1: String = "(2 * (1 + (10 + 10)))" Markus Hauck (codecentric AG) Functional Web Services slide 18
  19. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Review: Final Encoding sealed trait Exp[A] { def lit(x:Int): A def neg(e:A): A def add(e1 A, e2:A): A } new Exp[Int] { def lit(x:Int) = x def neg(e: Int) -e def add(e1:Int,e2:Int) = e1 + e2 } • define typeclass for syntax • define instance for semantics • languages are independent and composable • but: no AST. . . , what about optimizations? Markus Hauck (codecentric AG) Functional Web Services slide 19
  20. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Final Encoding: Transformations • in fact, we can still perform program transformations • example: push down negations • idea: move all “negations” into the leaves • -(1 + 2) → (-1 + -2) Markus Hauck (codecentric AG) Functional Web Services slide 20
  21. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Final Encoding: Transformations Markus Hauck (codecentric AG) Functional Web Services slide 21
  22. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Final Encoding: Transformations • we have to make the required context explicit • what do we need for pushing down negations? sealed trait Ctx case object P extends Ctx case object N extends Ctx // newtype for: Ctx => A final class Push[A](val run: Ctx => A) extends AnyVal Markus Hauck (codecentric AG) Functional Web Services slide 22
  23. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Final Encoding: Transformations def pushNeg[A](implicit E: Exp[A]): Exp[Push[A]] = new Exp[Push[A]] { def add(e1: Push[A], e2: Push[A]): Push[A] = new Push(ctx => E.add(e1.run(ctx),e2.run(ctx))) def neg(e: Push[A]): Push[A] = new Push({ case P => e.run(N) case N => e.run(P) }) def lit(x: Int): Push[A] = new Push({ case P => E.lit(x) case N => E.neg(E.lit(x)) }) } Markus Hauck (codecentric AG) Functional Web Services slide 23
  24. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Final Encoding: Transformations def pushNeg[A](implicit E: Exp[A]): Exp[Push[A]] = new Exp[Push[A]] { def add(e1: Push[A], e2: Push[A]): Push[A] = new Push(ctx => E.add(e1.run(ctx),e2.run(ctx))) } Markus Hauck (codecentric AG) Functional Web Services slide 23
  25. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Final Encoding: Transformations def pushNeg[A](implicit E: Exp[A]): Exp[Push[A]] = new Exp[Push[A]] { def neg(e: Push[A]): Push[A] = new Push({ case P => e.run(N) case N => e.run(P) }) } Markus Hauck (codecentric AG) Functional Web Services slide 23
  26. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Final Encoding: Transformations def pushNeg[A](implicit E: Exp[A]): Exp[Push[A]] = new Exp[Push[A]] { def lit(x: Int): Push[A] = new Push({ case P => E.lit(x) case N => E.neg(E.lit(x)) }) } Markus Hauck (codecentric AG) Functional Web Services slide 23
  27. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Final Encoding: Transformations def pushNeg[A](implicit E: Exp[A]): Exp[Push[A]] = new Exp[Push[A]] { def add(e1: Push[A], e2: Push[A]): Push[A] = new Push(ctx => E.add(e1.run(ctx),e2.run(ctx))) def neg(e: Push[A]): Push[A] = new Push({ case P => e.run(N) case N => e.run(P) }) def lit(x: Int): Push[A] = new Push({ case P => E.lit(x) case N => E.neg(E.lit(x)) }) } Markus Hauck (codecentric AG) Functional Web Services slide 23
  28. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Final Encoding: Performing Transformation scala> program[String] res0: String = (50 + -(6 + 2)) scala> program[Push[String]] res1: Push[String] = ... scala> program[Push[String]].run(P) res2: String = (50 + (-6 + -2)) Markus Hauck (codecentric AG) Functional Web Services slide 24
  29. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Final Encoding: Review • we can do transformations after all • (although still not as nice as pattern matching) • extension is possible without adapting existing code • so far, so good Markus Hauck (codecentric AG) Functional Web Services slide 25
  30. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s When You Have A Shiny New Hammer. . . Final Encoding ??? Markus Hauck (codecentric AG) Functional Web Services slide 26
  31. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Web Services with Http4s Markus Hauck (codecentric AG) Functional Web Services slide 27
  32. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s http4s What is http4s A typeful, purely functional, streaming library for HTTP clients and servers in Scala. • typeful — self-documentation and compile-time verification • purely functional — promote composability and reasoning • streaming — large payloads in constant space and websockets (currently being rewritten to use cats and fs2) Markus Hauck (codecentric AG) Functional Web Services slide 28
  33. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Writing Web Services With Http4s • define HttpService using the built-in DSL • really just Request => Task[Response] • (Task[A] is a better1 Future[A]) • define routes via pattern matching: HttpService { case req @ GET -> Root / "hello" => handleHelloWorld(req) } 1in the context of FP Markus Hauck (codecentric AG) Functional Web Services slide 29
  34. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Our Domain Runners Races Registrations Markus Hauck (codecentric AG) Functional Web Services slide 30
  35. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Our API GET "/runner/<runner-id>" POST "/runner" GET "/race/<race-id>" POST "/race" GET "/registration/<race-id>" PUT "/registration" Markus Hauck (codecentric AG) Functional Web Services slide 31
  36. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Our API HttpService { case GET -> Root / "runner" / RunnerIdVar(v) => ??? case req @ POST -> Root / "runner" => ??? case GET -> Root / "race" / RaceIdVar(v) => ??? case req @ POST -> Root / "race" => ??? case GET -> Root / "registration" / RaceIdVar(v) => ??? case req @ PUT -> Root / "registration" => ??? } Markus Hauck (codecentric AG) Functional Web Services slide 32
  37. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s The Idea • define a web service that uses a DSL • each request modeled as a program that is executed Markus Hauck (codecentric AG) Functional Web Services slide 33
  38. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s The Runners Paradise DSL trait RunnerAlg[F[_]] { def saveRunner(runner: Runner): F[Unit] def findRunner(id: RunnerId): F[Option[Runner]] } • we use a higher-kinded type F • kind: * -> *, i.e. it needs another type of kind * • results are wrapped in F[ ] • instances choose the concrete F Markus Hauck (codecentric AG) Functional Web Services slide 34
  39. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s The Runners Paradise DSL trait RaceAlg[F[_]] { def saveRace(race: Race): F[Unit] def findRace(id: RaceId): F[Option[Race]] } trait RegistrationAlg[F[_]] { def saveReg(reg: Registration): F[Unit] def findReg(id: RaceId): F[Option[Registration]] } Markus Hauck (codecentric AG) Functional Web Services slide 35
  40. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Let’s hammer • each route leads to a program being executed • the interpreter is left abstract, instantiate when running • let’s implement: registration of a runner for a race PUT "/registration" Markus Hauck (codecentric AG) Functional Web Services slide 36
  41. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s The Runners Pradise DSL: Registrations • allow registration of runners for races • if: • race exists • runner exists • race has free slots • reality: Either[RegistrationError,Registration] Markus Hauck (codecentric AG) Functional Web Services slide 37
  42. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Fair Warning: Fancy Code Incoming Markus Hauck (codecentric AG) Functional Web Services slide 38
  43. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s The Runners Paradise DSL def registerOpt[F[_]:Monad:RunnerAlg:RaceAlg:RegistrationAlg]( runnerId: RunnerId, raceId: RaceId): F[Option[Registration]] = { val M = Monad[OptionT[F, ?]] for { runner <- OptionT(RunnerAlg().findRunner(runnerId)) race <- OptionT(RaceAlg().findRace(raceId)) reg <- OptionT(RegistrationAlg().findReg(raceId)). orElse(M.point(Registration(race, Set()))) newReg <- OptionT(reg.add(runner).pure[F]) _ <- OptionT(RegistrationAlg().saveReg(newReg). map(Option(_))) } yield newReg }.run Markus Hauck (codecentric AG) Functional Web Services slide 39
  44. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Registration Program def registerOpt[F[_]: // still abstract Monad: // flatMap RunnerAlg: // runners RaceAlg: // races RegistrationAlg]( // registrations runnerId: RunnerId, // runner id raceId: RaceId // race id ): F[Option[Registration]] // wrapped in F Markus Hauck (codecentric AG) Functional Web Services slide 39
  45. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s The Runners Paradise DSL def registerOpt[F[_]:Monad:RunnerAlg:RaceAlg:RegistrationAlg]( runnerId: RunnerId, raceId: RaceId): F[Option[Registration]] = { val M = Monad[OptionT[F, ?]] // for { // runner <- OptionT(RunnerAlg().findRunner(runnerId)) // race <- OptionT(RaceAlg().findRace(raceId)) // reg <- OptionT(RegistrationAlg().findReg(raceId)). // orElse(M.point(Registration(race, Set()))) // newReg <- OptionT(reg.add(runner).pure[F]) // _ <- OptionT(RegistrationAlg().saveReg(newReg). // map(Option(_))) // } yield newReg }.run Markus Hauck (codecentric AG) Functional Web Services slide 39
  46. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s The Runners Paradise DSL def registerOpt[F[_]:Monad:RunnerAlg:RaceAlg:RegistrationAlg]( runnerId: RunnerId, raceId: RaceId): F[Option[Registration]] = { val M = Monad[OptionT[F, ?]] // for { runner <- OptionT(RunnerAlg().findRunner(runnerId)) // race <- OptionT(RaceAlg().findRace(raceId)) // reg <- OptionT(RegistrationAlg().findReg(raceId)). // orElse(M.point(Registration(race, Set()))) // newReg <- OptionT(reg.add(runner).pure[F]) // _ <- OptionT(RegistrationAlg().saveReg(newReg). // map(Option(_))) // } yield newReg }.run Markus Hauck (codecentric AG) Functional Web Services slide 39
  47. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s The Runners Paradise DSL def registerOpt[F[_]:Monad:RunnerAlg:RaceAlg:RegistrationAlg]( runnerId: RunnerId, raceId: RaceId): F[Option[Registration]] = { val M = Monad[OptionT[F, ?]] // for { // runner <- OptionT(RunnerAlg().findRunner(runnerId)) race <- OptionT(RaceAlg().findRace(raceId)) // reg <- OptionT(RegistrationAlg().findReg(raceId)). // orElse(M.point(Registration(race, Set()))) // newReg <- OptionT(reg.add(runner).pure[F]) // _ <- OptionT(RegistrationAlg().saveReg(newReg). // map(Option(_))) // } yield newReg }.run Markus Hauck (codecentric AG) Functional Web Services slide 39
  48. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s The Runners Paradise DSL def registerOpt[F[_]:Monad:RunnerAlg:RaceAlg:RegistrationAlg]( runnerId: RunnerId, raceId: RaceId): F[Option[Registration]] = { val M = Monad[OptionT[F, ?]] // for { // runner <- OptionT(RunnerAlg().findRunner(runnerId)) // race <- OptionT(RaceAlg().findRace(raceId)) reg <- OptionT(RegistrationAlg().findReg(raceId)). orElse(M.point(Registration(race, Set()))) // newReg <- OptionT(reg.add(runner).pure[F]) // _ <- OptionT(RegistrationAlg().saveReg(newReg). // map(Option(_))) // } yield newReg }.run Markus Hauck (codecentric AG) Functional Web Services slide 39
  49. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s The Runners Paradise DSL def registerOpt[F[_]:Monad:RunnerAlg:RaceAlg:RegistrationAlg]( runnerId: RunnerId, raceId: RaceId): F[Option[Registration]] = { val M = Monad[OptionT[F, ?]] // for { // runner <- OptionT(RunnerAlg().findRunner(runnerId)) // race <- OptionT(RaceAlg().findRace(raceId)) // reg <- OptionT(RegistrationAlg().findReg(raceId)). // orElse(M.point(Registration(race, Set()))) newReg <- OptionT(reg.add(runner).pure[F]) // _ <- OptionT(RegistrationAlg().saveReg(newReg). // map(Option(_))) // } yield newReg }.run Markus Hauck (codecentric AG) Functional Web Services slide 39
  50. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s The Runners Paradise DSL def registerOpt[F[_]:Monad:RunnerAlg:RaceAlg:RegistrationAlg]( runnerId: RunnerId, raceId: RaceId): F[Option[Registration]] = { val M = Monad[OptionT[F, ?]] // for { // runner <- OptionT(RunnerAlg().findRunner(runnerId)) // race <- OptionT(RaceAlg().findRace(raceId)) // reg <- OptionT(RegistrationAlg().findReg(raceId)). // orElse(M.point(Registration(race, Set()))) // newReg <- OptionT(reg.add(runner).pure[F]) _ <- OptionT(RegistrationAlg().saveReg(newReg). map(Option(_))) // } yield newReg }.run Markus Hauck (codecentric AG) Functional Web Services slide 39
  51. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s The Runners Paradise DSL def registerOpt[F[_]:Monad:RunnerAlg:RaceAlg:RegistrationAlg]( runnerId: RunnerId, raceId: RaceId): F[Option[Registration]] = { val M = Monad[OptionT[F, ?]] for { runner <- OptionT(RunnerAlg().findRunner(runnerId)) race <- OptionT(RaceAlg().findRace(raceId)) reg <- OptionT(RegistrationAlg().findReg(raceId)). orElse(M.point(Registration(race, Set()))) newReg <- OptionT(reg.add(runner).pure[F]) _ <- OptionT(RegistrationAlg().saveReg(newReg). map(Option(_))) } yield newReg }.run Markus Hauck (codecentric AG) Functional Web Services slide 39
  52. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Getting More Concrete: Instances • to actually run programs, we need to have instances • need to satisfy all constraints • pure in-memory, cassandra, postgres, redis, . . . Markus Hauck (codecentric AG) Functional Web Services slide 40
  53. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Instances for our DSL class Cass[A]( val value: ReaderT[Task, RunnersParadiseDb, A] ) extends AnyVal { def run: RunnersParadiseDb => Task[A] = value.run } • RunnersParadiseDb => Task[A] • given a connection to the DB, asynchronous results Markus Hauck (codecentric AG) Functional Web Services slide 41
  54. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Instances for our DSL class Cass[A]( val value: ReaderT[Task, RunnersParadiseDb, A] ) extends AnyVal { def run: RunnersParadiseDb => Task[A] = value.run } trait RunnerAlg[F[_]] { def saveRunner(runner: Runner): Cass[Unit] = new Cass(ReaderT(_.runners.save(runner).void)) // ... } Markus Hauck (codecentric AG) Functional Web Services slide 42
  55. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Putting It Together • remember signature of routes? Request => Task[Response] • at the end our program has to produce a Task • but we don’t want to be limited by this Markus Hauck (codecentric AG) Functional Web Services slide 43
  56. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s RaceRegistrationService Markus Hauck (codecentric AG) Functional Web Services slide 44
  57. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s RaceRegistrationService Markus Hauck (codecentric AG) Functional Web Services slide 44
  58. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s RaceRegistrationService Markus Hauck (codecentric AG) Functional Web Services slide 44
  59. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s The RaceRegistrationService case class Register(runner: RunnerId, race: RaceId) class RaceRegistrationService[F[_]:Monad :RunnerAlg :RaceAlg :RegistrationAlg]( natF: F ~> Task) { def service: HttpService = { def route = HttpService { // ... case req @ PUT -> Root / "registration" => handleRegistration(req) } } // private def handleRegistration ... } Markus Hauck (codecentric AG) Functional Web Services slide 45
  60. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s The RaceRegistrationService case class Register(runner: RunnerId, race: RaceId) class RaceRegistrationService[F[_]:Monad :RunnerAlg :RaceAlg :RegistrationAlg]( natF: F ~> Task) { private def handleRegistration(req: Req) = { req.decodeWith(jsonOf[Register]) { reg => natF(Programs.register[F]( reg.number, reg.race )) } } } Markus Hauck (codecentric AG) Functional Web Services slide 45
  61. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s So what? • okay we added a LOT of abstraction in our web service. . . • why should we do that? • composability • testability Markus Hauck (codecentric AG) Functional Web Services slide 46
  62. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Composability • we can choose interpreters and also mix them as we want: • remember push down negations? • logging • security • timeouts • tracing Markus Hauck (codecentric AG) Functional Web Services slide 47
  63. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Testability • use in-memory interpreter by only changing the type parameter • formulate laws on our type classes • use ScalaCheck to check for all instances (Cassandra, Redis, . . . ) • nice separation of domain logic that should not use the DSL Markus Hauck (codecentric AG) Functional Web Services slide 48
  64. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Review of Final Encodings • initial encoding: pattern matching and transformations more obvious • final encoding: easier to extend, same expressiveness • btw: nice way to solve expression problem (exercise) • you can always convert between initial and final Markus Hauck (codecentric AG) Functional Web Services slide 49
  65. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Final Encodings and Free Monads • final encoding + higher kinded type constructor. . . • similar to Free Monads, the latter use an initial encoding • the two are interchangeable • final encoding → free monads: type class instance that builds the free structure • free monads → final encoding: take in an instance and use it Markus Hauck (codecentric AG) Functional Web Services slide 50
  66. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Finalizing a Free Monad Program def finalize[R, F[_]:Monad:RunnerAlg]( p: FreeC[RunnerAlgFree, R]): F[R] = Free.runFC(p)(new (RunnerAlgFree ~> F) { def apply[A](fa: RunnerAlgFree[A]): F[A] = fa match { case SaveRunner(runner) => RunnerAlg[F].saveRunner(runner) case FindRunner(id) => RunnerAlg[F].findRunner(id) }}) Markus Hauck (codecentric AG) Functional Web Services slide 51
  67. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Free Monads vs Final Encoding • being able to convert is great • final encoding: easy to compose many languages, lower overhead • free monads: reify as first class values, pattern matching Markus Hauck (codecentric AG) Functional Web Services slide 52
  68. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Further References • Oleg Kiselyov: Lecture notes on Typed Tagless Final Interpreters • Runner’s Paradise Code: https://github.com/markus1189/runnersparadise • http4s: http://http4s.org/ Markus Hauck (codecentric AG) Functional Web Services slide 53
  69. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Questions Markus Hauck (codecentric AG) Functional Web Services slide 54