简体   繁体   English

是否可以替换为Scala宏中的案例类生成的默认apply方法?

[英]Is it possible to replace the default apply method generated for case classes in a Scala macro?

It seems like this doesn't work ( Using 2.11.1 and macro paradise 2.0.1). 看起来这不起作用(使用2.11.1和宏天堂2.0.1)。 I was hoping that the case class generated methods would either be suppressed, or be in the tree so I could get rid of it. 我希望case类生成的方法要么被抑制,要么在树中,所以我可以摆脱它。 Is this a hard limitation? 这是一个严格的限制吗?

class evis extends StaticAnnotation {
  def macroTransform(annottees: Any*) = macro EvisMacro.impl
}

object EvisMacro {

  def impl(c: blackbox.Context)(annottees: c.Expr[Any]*) : c.Expr[Any] = {
    import c.universe._

    def makeApply(tpName: TypeName, parents: List[Tree], params: List[List[ValDef]] ) : List[Tree]= {
      List(q"""def apply(...$params): $tpName = null""")
    }

    val result = annottees map (_.tree) match {
      case (classDef @ q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }")
        :: Nil if mods.hasFlag(Flag.CASE) =>
        c.info(c.enclosingPosition,  s"Eviscerating $tpname !($mods, $parents, $paramss)", true)

        parents match {
          case q"${pname: TypeName}" :: rest =>
            c.info(c.enclosingPosition, s"${pname.decodedName}", true )
            val sc = c.universe.rootMirror.staticClass( pname.decodedName.toString  )
            c.info(c.enclosingPosition, s"${sc}", true )
        }

        val name = tpname.toTermName
        q"""
        $classDef
        object $name {
          ..${makeApply(tpname, parents, paramss)}
        }
        """
      case (classDef @ q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }")
        :: q"object $objName {..$objDefs}"
        :: Nil if mods.hasFlag(Flag.CASE) =>
        q"""
        $classDef
         object $objName {
           ..${makeApply(tpname, parents, paramss)}
           ..$objDefs
         }
         """
      case _ => c.abort(c.enclosingPosition, "Invalid annotation target: must be a case class")
    }
    c.Expr[Any](result)
  }

}

Using it: 使用它:

trait Thing
@evis
case class Trade(id: Long, notional: Long, comment: String) extends Thing
@evis
case class Pop(name: String) extends Thing

object Pop{

}

object TestTrade extends App{

  val t = Trade (1, 1, "")
  val p : Pop = Pop("")

  println(t)

}

Results in: 结果是:

Error:(2, 2) method apply is defined twice conflicting symbols both originated in file 'core/src/main/scala/Test.scala' @evis ^ 错误:(2,2)方法apply定义了两个冲突的符号,这两个符号都起源于文件'core / src / main / scala / Test.scala'@evis ^

The problem is caused by the fact that, to the compiler, code generated by macro annotations isn't any different from code written by hand. 问题是由于这样一个事实,即对于编译器,宏注释生成的代码与手写的代码没有任何不同。 If you manually write the code produced by the macro provided in the example, you'll get exactly the same double definition error, so it's not a bug - it's a limitation of case class synthesis. 如果您手动编写示例中提供的宏生成的代码,您将获得完全相同的双定义错误,因此它不是错误 - 它是案例类合成的限制。 Unfortunately, case class synthesis is not extensible, so this needs to be worked around. 不幸的是,案例类合成是不可扩展的,因此需要解决这个问题。

One workaround that I'd propose is erasing the CASE flag from the mods of the class, and then the macro can be completely free in choosing which members to generate. 我建议的一个解决方法是从类的mod中删除CASE标志,然后宏可以完全自由地选择要生成的成员。 This, however, means that the macro will have to generate all the code that case classes typically generate, which is not going to be very pleasant. 但是,这意味着宏必须生成案例类通常生成的所有代码,这不会非常令人愉快。 Another caveat here is that the compiler treats pattern matching over CASE classes specially by emitting somewhat more efficient code, so such an emulation would also lose some performance (I'd reckon that the loss will be miniscule, and probably even non-existent with the new name-based pattern matching mechanism from Scala 2.11 - but that would need to be tested). 另一个需要注意的是,编译器通过发出更高效的代码来处理CASE类上的模式匹配,因此这样的仿真也会失去一些性能(我认为损失将是微不足道的,甚至可能不存在。 Scala 2.11中基于名称的新模式匹配机制 - 但需要对其进行测试)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM