LabelledGeneric获取类名

时间:2015-12-01 09:28:35

标签: scala shapeless

我对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 = ???
}

1 个答案:

答案 0 :(得分:7)

Shapeless的Generic为案例类和密封特征提供了产品总和表示,这意味着如果我们有这样一个简单的ADT:

sealed trait Base
case object Foo extends Base
case class Bar(i: Int, s: String) extends Base

然后Generic[Base]将给我们映射到Foo.type :+: Bar :+: CNil - 即。 Foo.type a Bar(其中表示我们在类型理论术语中讨论“和类型”),以及Generic[Bar]为我们提供了Int :: String :: HNil的映射,Int a String(产品类型,其中“产品”大致为与标准库中scala.ProductN类型的情况相同。

LabelledGeneric使用产品总和表示的增强版本,其中产品或总和中的每个术语都标有标签。在密封特征的情况下,这些将是每个子类型的构造函数名称,对于案例类,它们将是成员名称。这些不是完全限定的名称 - 只是在本地消除歧义的标签。

GenericLabelledGeneric无意用作编译时反射的通用工具。例如,它们不适用于任意类型,并且它们不提供对类型本身名称的访问。

您最好的选择可能是使用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 }"
  }
}

但是,如果你只需要价值级别的字符串,这对你的用例来说可能有点过分了。