Inferring implicit type parameters

January 31, 2017

Recently, I was working on MetaRouter, a Shapeless-based routing library for Scala. I wanted to create a function that takes a type parameter and resolves auxiliary ones via implicits. The function in question looked like this:

def route[T, ROUTE <: HList, Params <: HList]
  (route: Route[ROUTE])
  (implicit
    gen: Generic.Aux[T, Params],
    map: FlatMapper.Aux[Args.Convert.type, ROUTE, Params]
  ): MappedRoute[ROUTE, T] = new MappedRoute[ROUTE, T](route)

To give some context, this function is supposed to ensure that a case class provided as T matches the parameters of a route. The parameters of a route are its placeholders as an HList. For instance, Root / "details" / Arg[Int] has one placeholder which is in the terminal position.

The user should be able to call this function only by specifying T:

Router.route[Details](Root / "details" / Arg[Int])

Unfortunately, the above function would require us to also specify ROUTE and Params.

Poking around the source code of Shapeless I discovered a trick that allows you to bypass this step by creating a helper class with an apply() method:

private[metarouter] class MappedRouterHelper[T] {
  def apply[ROUTE <: HList, Params <: HList]
    (route: Route[ROUTE])
    (implicit
      gen: Generic.Aux[T, Params],
      map: FlatMapper.Aux[Args.Convert.type, ROUTE, Params]
    ): MappedRoute[ROUTE, T] =
      new MappedRoute[ROUTE, T](route)
}

def route[T] = new MappedRouterHelper[T]

When the user calls route[T], the types of our two HLists are inferred within the apply() call and do not need to be provided by the user.

Generated with MetaDocs v0.1.2-SNAPSHOT