Pass a type parameter to be used as argument LabelledGeneric

896 views Asked by At

I'm trying to parametrize a method that needs to work on a generic type A for which a LabelledGeneric 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 A:

type A is not a class or trait


So, I tried to have the LabelledGeneric inferred

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

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

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
  ...
}

Now, Repr is definitely an HList, but still Keys 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?

1

There are 1 answers

3
Miles Sabin On BEST ANSWER

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
  keys
}

// 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).