【问题标题】:How to get the name of a class as a string literal at compile time using shapeless?如何在编译时使用 shapeless 将类的名称作为字符串文字获取?
【发布时间】:2021-06-24 12:26:36
【问题描述】:

这是一个后续问题:

How to get the name of a case class field as a string/symbol at compile time using shapeless?

假设我要写一个可以转换产品类型的递归转换器:

case class Prod (
  a: Int,
  b: String
)

进入记录,但与使用每个案例类字段(a,b)作为键的上述问题不同,我想直接使用每个类名称或类型/类型构造函数名称。所以这个产品类型在编译时变成了一个记录:

"Int" ->> Int
"String" ->> String

(可能不是一个足够好的用例,但你明白了)

其中一个关键步骤是在编译时使用反射来获取每个类的名称,并将它们转换为 Singleton 类型或无形见证。我想知道这个功能是否已经在某处提供?还是我绝对需要一个白盒宏来实现它?

【问题讨论】:

    标签: scala shapeless scala-reflect singleton-type scala-2.13


    【解决方案1】:

    你必须写一个宏

    import shapeless.ops.hlist.Mapper
    import shapeless.{Generic, HList, Poly1, Typeable}
    import scala.language.experimental.macros
    import scala.reflect.macros.whitebox
    
    trait FieldTypes[A <: Product] {
      type Out <: HList
    }
    object FieldTypes {
      type Aux[A <: Product, Out0 <: HList] = FieldTypes[A] { type Out = Out0 }
    
      implicit def mkFieldTypes[A <: Product, L <: HList](implicit
        generic: Generic.Aux[A, L],
        mapper: Mapper[typeablePoly.type, L]
      ): Aux[A, mapper.Out] = null
    
      object typeablePoly extends Poly1 {
        implicit def cse[A](implicit typeable: Typeable[A]): Case[A] = macro cseImpl[A]
        def cseImpl[A: c.WeakTypeTag](c: whitebox.Context)(typeable: c.Tree): c.Tree = {
          import c.universe._
          val str = c.eval(c.Expr[String](c.untypecheck(q"$typeable.describe")))
          val tpA = weakTypeOf[A]
          q"null.asInstanceOf[FieldTypes.typeablePoly.Case.Aux[$tpA, _root_.shapeless.labelled.FieldType[$str, $tpA]]]"
        }
      }
    }
    

    测试:

    import shapeless.{HNil, ::}
    import shapeless.labelled.FieldType
    
    implicitly[FieldTypes.Aux[Prod, FieldType["Int", Int] :: FieldType["String", String] :: HNil]]
    

    【讨论】:

    • 哇,好的。我将寻求将您的代码合并到单例操作中(显然所有应得的功劳都得到了解决)。但是让我们先挂几个星期,看看是否会出现一个 dotty-friendly 的答案
    • 哦,还有一件事。答案可能有遗漏,当您将implicitly[... 赋值给某个值时,会导致编译错误:could not find implicit value for parameter e: org.shapesafe.m.FieldTypes.Aux[
    • @tribbloid 好吧,如果 implicitly[X] 编译而 val x = implicitly[X] 没有编译,这很奇怪。难道你忘了FieldTypesimplicitly... 必须在不同的子项目中(就像Scala 2 中的宏一样)?我无法在 Scastie 准备 sn-p,因为它不支持多个子项目。我可以在 Github 上发布。
    • 是的,这很奇怪 (github.com/tribbloid/shapesafe/runs/…) 可能是编译器错误。但是 Bloop/BSP 会很好地编译它
    猜你喜欢
    • 1970-01-01
    • 2011-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-30
    • 2021-12-04
    • 2015-03-13
    • 1970-01-01
    相关资源
    最近更新 更多