是否可以将抽象类或特征上的宏应用于所有子类?

Jac*_*nig 1 scala scala-macros

假设我Foo在某个库中具有某些特征(或抽象类),该特征通常由用户代码扩展并且需要某些方法numArgs

trait Foo {
  // Number of arguments to the default constructor
  def numArgs: Int
}
Run Code Online (Sandbox Code Playgroud)

现在numArgs写起来很简单,但是我想生成的是令人讨厌的样板。我可以通过反射来做到这一点,但是它很丑陋,并且无法移植到其他后端,例如ScalaJS,ScalaNative或GraalVM。

是否可以编写只能应用于Foo而不是在的每个子类上都需要)我可以应用的宏注释,Foo以便生成该方法?

Dmy*_*tin 6

不能。抽象类(或特征)上的宏注释只能更改此类(特征)及其伴随对象的AST。您不能更改父母,子女,兄弟姐妹等。这样做是为了使宏注释在此意义上是本地的。

而且,如果未密封特征,则甚至在编译时也不知道其子类。

如果将类(特征)和子类嵌套到某个对象中,则可以注释该对象。

如果要修改任意树,则可能需要通过Scalameta进行编译器插件或代码生成(源代码生成)。

我想在您的用例中,您可以将宏注释替换为def宏

import scala.language.experimental.macros
import scala.reflect.macros.blackbox

def numArgs[A]: Int = macro impl[A]

def impl[A: c.WeakTypeTag](c: blackbox.Context): c.Tree = {
  import c.universe._
  val numArgs = weakTypeOf[A].typeSymbol.asClass.primaryConstructor.asMethod.paramLists.flatten.length
  q"$numArgs"
}

class A(i: Int, s: String)

numArgs[A] // 2
Run Code Online (Sandbox Code Playgroud)

或Shapeless(如果子类是案例类)

import shapeless.ops.hlist.Length
import shapeless.ops.nat.ToInt
import shapeless.{::, Generic, HList, HNil, Nat}

def numArgs[A] = new PartiallyApplied[A]

class PartiallyApplied[A] {
  def apply[L <: HList, N <: Nat]()(implicit gen: Generic.Aux[A, L], length: Length.Aux[L, N], toInt: ToInt[N]): Int = toInt()
}

case class A(i: Int, s: String)

numArgs[A]() // 2
Run Code Online (Sandbox Code Playgroud)