Gabriele Petronella Gabriele Petronella - 10 months ago 50
Scala Question

Pass a type parameter to be used as argument LabelledGeneric

I'm trying to parametrize a method that needs to work on a generic type

for which a
can be retrieved. Here's the naive approach

case class Foo(bar: String, baz: Boolean)

def params[A](a: A) = {
val lbl = LabelledGeneric[A]
val keys = Keys[lbl.Repr].apply

val myThingy = params(Foo)

Of course, the underlying macro complains. It doesn't know enough about

type A is not a class or trait

So, I tried to have the

def params[A](a: A)(implicit lbl: LabelledGeneric[A]) = {
val keys = Keys[lbl.Repr].apply

this seems to work, but the
type is not known to be an

type arguments [lbl.Repr] do not conform to method apply's type parameter bounds [L <: shapeless.HList]

Ok, let's try to be more precise

def params[A, Repr <: HList](a: A)(implicit lbl: LabelledGeneric.Aux[A, Repr]) = {
val keys = Keys[lbl.Repr].apply

is definitely an
, but still
cannot resolve its implicits

could not find implicit value for parameter values: shapeless.ops.record.Values[lbl.Repr]

Final attempt, let's try to have everything I need computed implicitly

def params[A, Repr <: HList](a: A)(implicit
lbl: LabelledGeneric.Aux[A, Repr],
kk: Keys[Repr]
) = {
val keys = kk.apply

Still no luck, apparently the first implicit cannot be resolved at call site

could not find implicit value for parameter lbl: shapeless.LabelledGeneric.Aux[example.Main.Foo.type,Repr]

[error] params(Foo)

Clearly all of this machinery works, when dealing directly with the specific type, e.g.

val lbl = LabelledGeneric[Foo]
val keys = Keys[lbl.Repr].apply
// no problem

I'm clearly missing the needed set of refinements on my type in the method signature, but I can get my head around what's going on here. Any idea?

Answer Source

The last variant with everything computed implicitly works for me,

scala> import shapeless._, ops.record._
import shapeless._
import ops.record._

scala> :paste
// Entering paste mode (ctrl-D to finish)

def params[A, Repr <: HList](a: A)
  (implicit lbl: LabelledGeneric.Aux[A, Repr], kk: Keys[Repr]) = {
  val keys = kk.apply

// Exiting paste mode, now interpreting.

params: ...

scala> case class Foo(bar: String, baz: Boolean)
defined class Foo

scala> params(foo)
res0: ... = 'bar :: 'baz :: HNil

(result types elided for readability).