LabelledGeneric to get class name
scala, shapeless
Solution
Shapeless's `Generic` provides a sum-of-products representation for case classes and sealed traits, which means that if we have a simple ADT like this:
sealed trait Base
case object Foo extends Base
case class Bar(i: Int, s: String) extends Base
Then `Generic[Base]` will give us a mapping to a `Foo.type :+: Bar :+: CNil`—i.e. a `Foo.type` or a `Bar` (where the or means we're talking about a "sum type" in type theoretic terms), and a `Generic[Bar]` gives us a mapping to an `Int :: String :: HNil`, which is `Int` and a `String` (a product type, where "product" has roughly the same meaning that it does in the case of the `scala.ProductN` types in the standard library).
`LabelledGeneric` uses an enhanced version of the sum-of-products representation, where each of the terms in the product or sum is tagged with a label. In the case of a sealed trait, these will be the constructor names for each subtype, and in the case of a case class they'll be the member names. These aren't fully-qualified names—just labels that disambiguate locally.
`Generic` and `LabelledGeneric` aren't intended to serve as general-purpose tools for compile-time reflection. They're not available for arbitrary types, for example, and they don't provide access to the name of the type itself.
Your best bet is probably to use `TypeTag`, but if you want a type-level representation of the name (like `LabelledGeneric` provides for labels), you'll need to define your own type class with macro-generated instances. Something like the following should work:
import scala.language.experimental.macros
import scala.reflect.macros.whitebox.Context
trait TypeInfo[A] { type Name <: String; def name: String }
object TypeInfo {
type Aux[A, Name0 <: String] = TypeInfo[A] { type Name = Name0 }
def apply[A](implicit ti: TypeInfo[A]): Aux[A, ti.Name] = ti
implicit def materializeTypeInfo[A, Name <: String]: Aux[A, Name] =
macro matTypeInfoImpl[A, Name]
def matTypeInfoImpl[A: c.WeakTypeTag, Name <: String](c: Context): c.Tree = {
import c.universe._
val A = c.weakTypeOf[A]
val name = A.typeSymbol.name.decodedName.toString.trim
q"new TypeInfo[$A] { type Name = ${ Constant(name) }; def name = $name }"
}
}
This is probably overkill for your use case, though, if you only need value-level strings.
Problem
I'm fairly new to Shapeless, as one will infer from my question. Given an instance of `LabelledGeneric`, how do I get the name of the class that it represents. I can get the field name information from `Keys`, so I assume I need some other kind of `Witness` that encapsulates the type itself, but I cannot figure out which. Eg, if I have a case class called Foo in package com.bar, I want to get the string "com.bar.Foo" (or separately is fine). ``` implicit def example[T, Repr <: HList](implicit label: LabelledGeneric.Aux[T, Repr], kk: Keys[Repr]): Bibble[T] = new Bibble[T] { override def typeName(value: T): String = ??? } ```