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

Functional Web Services

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.
Avatar for Markus Hauck Markus Hauck
February 28, 2017

Functional Web Services

Use EDSLs and the final encoding to write a web service

Avatar for Markus Hauck

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