class: center, middle # Finch and Shapeless
@travisbrown
Twitter, Inc.
FinagleCon
2015
--- layout: true --- .left-column[ ## Shapeless ] .right-column[ ### What is [Shapeless](https://github.com/milessabin/shapeless/)? A library for generic programming in Scala: * Abstraction over tuples * Abstraction over sum types (arbitrary-arity `Either`) * Abstraction over case classes, ADTs * Statically sized collections * Type-level numbers, strings, etc. Ever wanted to concatenate tuples? Shapeless is for you! ] --- .left-column[ ## Shapeless ## Readers ] .right-column[ ### Shapeless in Finch: request readers ```scala import io.finch.request._ case class User(id: Long, name: String) val reader: RequestReader[User] = (param("id").as[Long] :: param("name")).as[User] ``` Or even better: ```scala val reader = RequestReader.derive[User].fromParams ``` No runtime reflection, no boilerplate! ] --- .left-column[ ## Shapeless ## Readers ## Routers ] .right-column[ ### Shapeless in Finch: coproduct routers The problem: * We want to be able to compose routers * Routers may return different types * Making all routers return HTTP responses is verbose ```scala def endpointA: Router[Response] def endpointB: Router[Response] def composed: Router[Response] = endpointA orElse endpointB ``` vs. ```scala def endpointA: Router[A] def endpointB: Router[B] def composed: Router[?] = endpointA ? endpointB ``` ] --- .left-column[ ## Shapeless ## Readers ## Routers ] .right-column[ ### Coproducts to the rescue What is a coproduct? * Like `Either`, but any number of branches * Can be concatenated, mapped over, etc. * Built with the "space invader" operator: `:+:` ```scala def endpointA: Router[A] def endpointB: Router[B] def endpointC: Router[C] def composed = endpointA :+: endpointB :+: endpointC ``` ] --- .left-column[ ## Shapeless ## Readers ## Routers ] .right-column[ ### What's this good for? Suppose we know how to turn `A`, `B`, and `C` into responses ```scala import com.twitter.finagle.Service def endpointA: Router[A] def endpointB: Router[B] def endpointC: Router[C] def composed = endpointA :+: endpointB :+: endpointC def service: Service[Request, Response] = composed.toService ``` ### So what? * We get to use meaningful types for our endpoints * No need to take every router all the way to `Response` * Completely safe and statically checked ] --- .left-column[ ## Shapeless ## Readers ## Routers ## Circe ] .right-column[ ### [Circe](https://github.com/travisbrown/circe) * Yet another JSON library for Scala * A fork of [Argonaut](http://argonaut.io/) * Type class-based encoding and decoding * Zippers for concise, immutable JSON transformations * No runtime reflection anywhere ] --- .left-column[ ## Shapeless ## Readers ## Routers ## Circe ] .right-column[ ### [Circe](https://github.com/travisbrown/circe) * Yet another JSON library for Scala * A fork of [Argonaut](http://argonaut.io/) * Type class-based encoding and decoding * Zippers for concise, immutable JSON transformations * No runtime reflection anywhere ### Omg why are you doing this to us * Modularity: minimal core * Parsing via [Jawn](https://github.com/non/jawn): fast, async parsing support * Codec derivation via Shapeless ```scala case class User(id: Long, name: String, email: String) body.as[User] ``` ] --- .left-column[ ## Shapeless ## Readers ## Routers ## Circe ] .right-column[ ### ["Incomplete"](https://meta.plasm.us/posts/2015/06/21/deriving-incomplete-type-class-instances/) case class decoders Our model: ```scala case class User(id: Long, name: String, email: String) ``` Incoming JSON from `POST` request: ```json { "name": "Foo McBar", "email": "foo@mcbar.com" } ``` Boilerplate-y solution: ```scala case class PartialUser(name: String, email: String) ``` Circe + Finch: ```scala body.as[Long => User] ``` ] --- .left-column[ ## Shapeless ## Readers ## Routers ## Circe ] .right-column[ ### ["Patching"](https://twitter.com/travisbrown/status/631134858362798080) case class decoders Our model: ```scala case class User(id: Long, name: String, email: String) ``` Incoming JSON from `PATCH` request: ```json { "email": "foo@mcbar.com" } ``` Boilerplate-y solution: ```scala case class PatchedUser( name: Option[String], email: Option[String] ) ``` Circe + Finch: ```scala body.as[User => User] ``` ] --- .left-column[ ## Shapeless ## Readers ## Routers ## Circe ## The end ] .right-column[ ## Thanks! ]