【问题标题】:Generate companion object for case class with methods (field = method)使用方法(字段=方法)为案例类生成伴随对象
【发布时间】:2019-08-31 17:27:52
【问题描述】:

使用 scala-macros 为案例类生成伴随对象

我尝试了一些代码示例,它可以获取元组列表(名称 -> 类型),但是如何在同一范围内生成对象?

  import c.universe._
  val tpe = weakTypeOf[T]

  val fields = tpe.decls.collectFirst {
    case m: MethodSymbol if m.isPrimaryConstructor => m
  } .get
    .paramLists
    .head

  val extractParams = fields.map { field =>
    val name = field.asTerm.name
    val fieldName = name.decodedName.toString
    val NullaryMethodType(fieldType) = tpe.decl(name).typeSignature

    c.Expr[List[(String, String)]](
      q"""
       ($fieldName, ${fieldType.toString})
      """
    )

是否可以注释某些案例类和生成的伴侣在同一范围内可见?

// test case: defined a class with some fields
@GenerateCompanionWithFields
case class SomeCaseClass(i: Int, b: Byte, c: Char)

目标:

SomeCaseClass.i() // returns Int 
SomeCaseClass.b() // returns Byte
SomeCaseClass.c() // returns Char

【问题讨论】:

    标签: scala scala-macros companion-object scala-2.13


    【解决方案1】:

    您的代码似乎适用于def macros。但是如果你想生成同伴,你应该使用macro annotations

    import scala.annotation.{StaticAnnotation, compileTimeOnly}
    import scala.language.experimental.macros
    import scala.reflect.macros.whitebox
    
    object Macros {
      @compileTimeOnly("enable macro paradise")
      class GenerateCompanionWithFields extends StaticAnnotation {
        def macroTransform(annottees: Any*): Any = macro Macro.impl
      }
    
      object Macro {
        def impl(c: whitebox.Context)(annottees: c.Tree*): c.Tree = {
          import c.universe._
          annottees match {
            case (cls @ q"$_ class $tpname[..$_] $_(...$paramss) extends { ..$_ } with ..$_ { $_ => ..$_ }") :: Nil =>
    
              val newMethods = paramss.flatten.map {
                case q"$_ val $tname: $tpt = $_" =>
                  q"def $tname(): String = ${tpt.toString}"
              }
    
              q"""
                 $cls
    
                 object ${tpname.toTermName} {
                   ..$newMethods
                 }
               """
          }
        }
      }
    }
    
    import Macros._
    
    object App {
      @GenerateCompanionWithFields
      case class SomeCaseClass(i: Int, b: Byte, c: Char)
    }
    
    //Warning:scalac: {
    //  case class SomeCaseClass extends scala.Product with scala.Serializable {
    //    <caseaccessor> <paramaccessor> val i: Int = _;
    //    <caseaccessor> <paramaccessor> val b: Byte = _;
    //    <caseaccessor> <paramaccessor> val c: Char = _;
    //    def <init>(i: Int, b: Byte, c: Char) = {
    //      super.<init>();
    //      ()
    //    }
    //  };
    //  object SomeCaseClass extends scala.AnyRef {
    //    def <init>() = {
    //      super.<init>();
    //      ()
    //    };
    //    def i(): String = "Int";
    //    def b(): String = "Byte";
    //    def c(): String = "Char"
    //  };
    //  ()
    //}
    

    automatically generate case object for case class

    【讨论】:

    • 谢谢!但我有一个小问题,这段代码无法编译 ${tpname.toTermName} - 无法解析“toTermName”我的项目结构:项目----核心----宏
    • @Code_VM 你确定吗?你使用 IntelliJ 吗?对我来说,在 Scala 2.13.0 IntelliJ 中,toTermName 显示为红色,但一切都可以编译。试试sbt compile。您是否添加了“scala-reflect”库依赖项?您的项目结构看起来不错。通常,您有宏的子项目(“宏”)和使用宏的子项目(“核心”)。
    • @Code_VM 你可以尝试用TermName(tpname.toString)替换tpname.toTermName
    • 是的,我对红色的术语名称有完全相同的问题,是的,我使用 IntelliJ。当然宏和核心子项目是按照你的描述声明的
    • @Code_VM IntelliJ 有时会以红色突出显示 Scala 代码(依赖类型、宏等),这没关系。重要的是代码是否编译。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-17
    • 1970-01-01
    • 1970-01-01
    • 2020-06-08
    相关资源
    最近更新 更多