finch, shapeless, and cats

Travis Brown
@travisbrown

what is shapeless?

why shapeless?


scala> import shapeless.syntax.std.tuple._
import shapeless.syntax.std.tuple._

scala> ('foo, "bar") ++ (1, 2.0, 3L)
res0: (Symbol, String, Int, Double, Long) = ('foo,bar,1,2.0,3)
					

why shapeless?


import shapeless._, ops.hlist.Align
class SameFieldsConverter[T] {
  def apply[S, SR <: HList, TR <: HList](s: S)(implicit
    genS: LabelledGeneric.Aux[S, SR],
    genT: LabelledGeneric.Aux[T, TR],
    align: Align[SR, TR]) = genT.from(align(genS.to(s)))
}
def convertTo[T] = new SameFieldsConverter[T]

case class A(foo: Int, bar: String)
case class B(bar: String, foo: Int)

val b: B = convertTo[B](A(12, "foo"))
					

You could do this with a macro, but...

...the best macro is a macro you don't maintain

the fun (okay, frivolous) side

shapeless in action

what is cats?

  • Functional programming abstractions in Scala
  • Like scalaz, but:
    • Less boilerplate
    • More performance
    • Less fundamentalist
    • More compromise
  • Only three months old

why cats?

We want to avoid rolling our own:

  • Reader monad
  • Validation type
  • Monoid
  • And on and on...

big picture

  • Less code duplication
  • Less boilerplate
  • More type safety
  • Elegant APIs

check them out!

shapeless and finch

Building RequestReader with ~ (before)


import io.finch.request._

case class User(id: Long, name: String)

val reader: RequestReader[User] =
  param("id").as[Long] ~ param("name") ~> User
					

This is awesome, but...

drawbacks of ~

shapeless style

Building RequestReader with :: (now)


val reader: RequestReader[User] =
  (param("id").as[Long] :: param("name")).as[User]
					

Now in finch

advantages of ::

  • Syntax for building HList readers
  • No more boilerplate
  • Richer case class support

derive it all!


val reader: RequestReader[User] =
  RequestReader.to[User].fromParams
					

Out for review

what's happening here?

  • A macro analyzes the case class
  • We create param readers for each field name
  • We gather DecodeRequest instances for field types
  • We compose it all into a request reader

All at compile time

cats and finch

  • PRequestReader is a monad
  • Specifically it's a reader monad
  • A wrapper for A => Future[?]

request readers and kleisli arrows

kleisli arrows

  • (P)RequestReader are type aliases
  • No more map, flatMap
  • embedFlatMap is flatMapK, etc.

other stuff

  • Custom toFuture becomes pure[Future]
  • Validated for error accumulation
  • And so on...

catbird

advantages

  • Less code to maintain
  • Shared types and names
  • Minimal API changes (mostly improvements)

next steps

concerns

  • Performance
  • Complexity for users
  • Dependencies
  • Java compatibility

resources

thanks!