Is there a boilerplate-free way to convert HLists to an argument list?

180 views Asked by At

According to this question Spray uses sbt-boilerplate to generate ApplyConverter instances that implicitly convert A :: B :: ... :: HNil => R to the more traditional (A, B, ...) => R for ease of use. That question was asked over a year ago; can this now be done using, for example, macros or Shapeless's newer functionality so that an external code generation step isn't required?

2

There are 2 answers

0
Miles Sabin On BEST ANSWER

This has been included in shapeless for a very long time. In shapeless 2.0.0 you can do the following,

scala> import shapeless._, syntax.std.function._
import shapeless._
import syntax.std.function._

scala> val f1: (Int, String, Boolean) => Int = (i, s, b) => i+s.length+(if(b) 1 else 0)
f1: (Int, String, Boolean) => Int = <function3>

scala> val pf1 = f1.toProduct
pf1: Int :: String :: Boolean :: HNil => Int = <function1>

scala> pf1(23 :: "foo" :: true :: HNil)
res0: Int = 27

scala> val pf2: (Int :: String :: HNil) => Int = l => l.head+l.tail.head.length
pf2: Int :: String :: HNil => Int = <function1>

scala> val f2 = pf2.fromProduct
f2: (Int, String) => Int = <function2>

scala> f2(23, "foo")
res1: Int = 26

(REPL result type rendering tidied for readability).

7
lmm On

I think there is no way to do this in pure Scala, as there is no way to generically talk about the FunctionN interfaces. At the macro level it would be possible, but not in a way that makes use of the AST representation available; a macro to do this would probably be reduced to (effectively) doing string manipulation - in which case there's very little advantage over sbt-boilerplate.

Even if it did become possible, Spray is a fairly foundational library that needs to be usable in conjunction with other libraries (thus it continues to build releases against old versions of Scala and Akka) - something that will only become more important if and when Play is ported to run on top of Spray. So I would expect it will be a while before Spray introduces a hard dependency on Shapeless 2 (which is incompatible with Shapeless 1), and until then I imagine they'll try and keep the code for the shapeless2 build of spray-routing as close as possible to that for the shapeless1 build for ease of maintenance.