我对Shapeless很新,因为我会从我的问题中推断出来.给定一个实例LabelledGeneric,如何获取它所代表的类的名称.我可以从中获取字段名称信息Keys,所以我假设我需要一些其他类型的Witness封装类型本身,但我无法弄清楚哪个.
例如,如果我在包com.bar中有一个名为Foo的case类,我想得到字符串"com.bar.Foo"(或单独就可以了).
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 = ???
}
Run Code Online (Sandbox Code Playgroud)
Shapeless Generic为案例类和密封特征提供了产品总和表示,这意味着如果我们有一个像这样的简单ADT:
sealed trait Base
case object Foo extends Base
case class Bar(i: Int, s: String) extends Base
Run Code Online (Sandbox Code Playgroud)
然后Generic[Base]将给我们一个映射到Foo.type :+: Bar :+: CNil-ie a Foo.type 或 a Bar(其中或者意味着我们在类型理论术语中讨论"和类型"),并且a Generic[Bar]给我们映射到a Int :: String :: HNil,即a Int 和 a String(产品) type,其中"product" scala.ProductN与标准库中的类型具有大致相同的含义.
LabelledGeneric使用产品总和表示的增强版本,其中产品或总和中的每个术语都用标签标记.在密封特征的情况下,这些将是每个子类型的构造函数名称,并且在案例类的情况下,它们将是成员名称.这些不是完全限定的名称 - 只是在本地消除歧义的标签.
Generic并且LabelledGeneric不打算充当通用的工具,编译时反射.例如,它们不适用于任意类型,并且它们不提供对类型本身名称的访问.
您最好的选择可能是使用TypeTag,但如果您想要名称的类型级别表示(如LabelledGeneric提供标签),则需要使用宏生成的实例定义自己的类型类.像下面这样的东西应该工作:
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 }"
}
}
Run Code Online (Sandbox Code Playgroud)
但是,如果您只需要值级别的字符串,这对您的用例来说可能有些过分.