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

Functional Web Services

Markus Hauck
February 28, 2017

Functional Web Services

Use EDSLs and the final encoding to write a web service

Markus Hauck

February 28, 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 Testing 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 Testing 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 Testing What are we going to do tonight • write a small web service • with http4s • using an embedded DSL • emphasize functional style 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 Testing Embedded DSLs • embedded in general purpose language (host language) • powerful: separate description of program from execution • initial encoding: Free Monads • final encoding: typeclasses + instances • also: deep vs shallow embedding 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 Testing A First DSL • let’s start with our first DSL • arithmetic operations: + and - (negation) • how would you do this in an initial encoding? 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 Testing Initial Encoding 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 //(8 + -(1 + 2)) Add(Lit(8), Neg(Add(Lit(1), 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 Testing Initial Encoding DSL AST Interpreter 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 Testing Properties of the Initial Encoding • nice to have ADT with possible cases • flexible interpretation, evaluate, pretty print, . . . • bad: extending with e.g. multiplication is not independent • let’s try something different: final encoding • still embedded DSL, get rid of AST 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 Testing Final Encoding DSL AST Interpreter 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 Testing Live Coding: Our First DSL 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 Testing 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 • typeclass also called Symantics • languages are independent, expressed via constraints on types 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 Testing Web Services with Http4s 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 Testing 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 13
  14. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Testing Writing Web Services With Http4s • define HttpService using the built-in DSL • really just Request => Task[Response] • (Task[A] is a better Future[A]) • define routes via pattern matching: HttpService { case req @ GET -> Root / "hello" => handleHelloWorld(req) } 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 Testing Our Domain Runners Races Registrations 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 Testing Our API GET "localhost/runner/<runner-id>" POST "localhost/runner" GET "localhost/race/<race-id>" POST "localhost/race" GET "localhost/registration/<race-id>" PUT "localhost/registration" 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 Testing 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 17
  18. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Testing 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 18
  19. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Testing 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 19
  20. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Testing 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 20
  21. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Testing Fair Warning: Fancy Code Incoming 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 Testing 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 22
  23. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Testing 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 23
  24. Intro A First DSL Web Services With Http4s Runner’s Paradise

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

    DSL Final Encodings and http4s Testing 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 25
  26. Intro A First DSL Web Services With Http4s Runner’s Paradise

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

    DSL Final Encodings and http4s Testing 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 27
  28. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Testing Let’s Have A Look Markus Hauck (codecentric AG) Functional Web Services slide 28
  29. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Testing Typeclasses Need Laws • typeclasses should always come with laws • what about RunnerAlg? trait RunnerAlg[F[_]] { def saveRunner(runner: Runner): F[Unit] def findRunner(id: RunnerId): F[Option[Runner]] def listRunner: F[Vector[Runner]] } Markus Hauck (codecentric AG) Functional Web Services slide 29
  30. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Testing Typeclasses Need Laws • typeclasses should always come with laws • what about RunnerAlg? trait RunnerAlg[F[_]] { def saveRunner(runner: Runner): F[Unit] def findRunner(id: RunnerId): F[Option[Runner]] def listRunner: F[Vector[Runner]] } • saveRunner *> saveRunner • findRunner *> findRunner • saveRunner *> findRunner • saveRunner *> listRunners Markus Hauck (codecentric AG) Functional Web Services slide 29
  31. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Testing ScalaCheck • ScalaCheck is perfect for checking our laws • defined in terms of RunnerAlg[F[ ]] forAll { (runner: Runner) => run { RunnerAlg().saveRunner(runner) *> RunnerAlg().findRunner(runner.id) }.value should ===(runner) } • instantiate this test for our instances • pure in memory, cassandra, postgres, . . . Markus Hauck (codecentric AG) Functional Web Services slide 30
  32. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Testing 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 31
  33. Intro A First DSL Web Services With Http4s Runner’s Paradise

    DSL Final Encodings and http4s Testing 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 32
  34. Intro A First DSL Web Services With Http4s Runner’s Paradise

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