Scala 3:查找具有给定注释的函数

问题描述 投票:0回答:2

对于 Scala 3 宏,有谁知道如何查找具有给定注释的所有函数吗?

例如:

@fruit
def apple(): Int = ???

@fruit
def banana(): Int = ???

@fruit
def coconut(): Int = ???

@fruit
def durian(): Int = ???

def elephant(): Int = ???

@fruit
def fig(): Int = ???

我想找到

apple, banana, coconut, durian, fig
的列表。它们可以在任何地方定义,但在我的例子中,它们都将在一个包中。

scala metaprogramming scala-macros scala-3
2个回答
4
投票

此解决方案将从给定包中提取带有某些注释的所有定义。我还将利用编译时反射。

此解决方案将从给定包中提取带有一些注释的所有定义。我还将利用编译时反射。 因此,为了解决您的问题,我们需要将其分为:

  • 从包中收集方法;
  • 仅过滤具有给定注释的方法;
  • 在函数应用中变换符号。 我想您可以将包和注释(以及返回类型)作为类型参数传递。所以宏签名是这样的:
inline def findAllFunction[P, A <: ConstantAnnotation, R]: List[() => R] = 
    ${Implementation.myMacroImpl[P, A, R]()}

第一点很简单。我们可以提取所有定义为的方法:

def methodsFromPackage(packageSymbol: Symbol): List[Symbol] =
  packageSymbol.declaredTypes
    .filter(_.isClassDef)
    .flatMap(_.declaredMethods)

第二点也很简单。

Symbol
类具有可以在这种情况下使用的方法
hasAnnotation

def methodsAnnotatatedWith(
    methods: List[Symbol],
    annotation: Symbol
): List[Symbol] =
  methods.filter(_.hasAnnotation(annotation))

最后一点有点挑战性。这里我们应该构造方法调用。所以我们需要创建与方法调用相对应的 AST。受这个示例的启发,我们可以使用

Apply
来调用定义。
Select
This
用于选择要调用的正确方法:

def transformToFunctionApplication(methods: List[Symbol]): Expr[List[() => R]] =
  val appliedDef = methods
    .map(definition => Select(This(definition.owner), definition))
    .map(select => Apply(select, List.empty))
    .map(apply => '{ () => ${ apply.asExprOf[R] } })
  Expr.ofList(appliedDef)

这里我使用了lambda调用,如果你想直接返回值你应该改变最后两条指令:

def transformToFunctionApplication(methods: List[Symbol]): Expr[List[R]] =
  val appliedDef = methods
    .map(definition => Select(This(definition.owner), definition))
    .map(select => Apply(select, List.empty))
    .map(apply => apply.asExprOf[R])

  Expr.ofList(appliedDef)

总而言之,所有方法可以定义为:

def myMacroImpl[P: Type, A: Type, R: Type]()(using
    Quotes
): Expr[List[() => R]] = {
  import quotes.reflect.*
  val annotation = TypeRepr.of[A].typeSymbol
  val moduleTarget = TypeRepr.of[P].typeSymbol

  def methodsFromPackage(packageSymbol: Symbol): List[Symbol] =
    packageSymbol.declaredTypes
      .filter(_.isClassDef)
      .flatMap(_.declaredMethods)

  def methodsAnnotatatedWith(
      methods: List[Symbol],
      annotation: Symbol
  ): List[Symbol] =
    methods.filter(_.hasAnnotation(annotation))

  def transformToFunctionApplication(
      methods: List[Symbol]
  ): Expr[List[() => R]] =
    val appliedDef = methods
      .map(definition => Select(This(definition.owner), definition))
      .map(select => Apply(select, List.empty))
      .map(apply => '{ () => ${ apply.asExprOf[R] } })
    Expr.ofList(appliedDef)

  val methods = methodsFromPackage(moduleTarget)
  val annotatedMethod = methodsAnnotatatedWith(methods, annotation)
  transformToFunctionApplication(annotatedMethod)
}

最后,您可以将宏用作:

package org.tests
import org.tests.Macros.fruit

package foo {
  @fruit
  def check(): Int = 10
  @fruit
  def other(): Int = 11
}


@main def hello: Unit = 
  println("Hello world!")
  println(Macros.findAllFunction[org.tests.foo, fruit, Int].map(_.apply())) /// List(10, 11)

斯卡斯蒂


0
投票

我认为你必须使用 Scala 3 中的 Tasty Inspector。 因此,为了分析代码,您使用 Tasty Inspector,为了调用等,您使用宏。 此代码仅适用于基元和基元集合

© www.soinside.com 2019 - 2024. All rights reserved.