【问题标题】:Passing type parameter to scala meta macro/annotations将类型参数传递给 scala 元宏/注释
【发布时间】:2017-01-21 18:05:56
【问题描述】:
package scalaworld.macros
import scala.meta._

class Argument(arg: Int) extends scala.annotation.StaticAnnotation {
  inline def apply(defn: Any): Any = meta {
    println(this.structure)
    val arg = this match {
      // The argument needs to be a literal like `1` or a string like `"foobar"`.
      // You can't pass in a variable name.
      case q"new $_(${Lit(arg: Int)})"                      => arg
      // Example if you have more than one argument.
      case q"new $_(${Lit(arg: Int)}, ${Lit(foo: String)})" => arg
      case _                                                => ??? // default     value
    }
    println(s"Arg is $arg")
    defn.asInstanceOf[Stat]
  }
}

我想修改上面的宏并添加类型参数[A]。 我尝试了以下但它没有编译

package scalaworld.macros
import scala.meta._

class Argument2[A](arg: A) extends scala.annotation.StaticAnnotation {
  inline def apply(defn: Any): Any = meta {
    println(this.structure)
    val arg = this match {
      case q"new $_(${Lit(arg: A)})"                      => arg
      case q"new $_(${Lit(arg: A)}, ${Lit(foo: String)})" => arg
      case _                                              => ???
    }
    println(s"Arg is $arg")
    defn.asInstanceOf[Stat]
  }
}

【问题讨论】:

标签: scala scala-macros scalameta


【解决方案1】:

传入宏注解的参数作为元树传入。

尽管可以通过 Lit() 提取器提取诸如 Int/Double/String 之类的文字。这不是其他事情的情况。

在元中解析时

  • @someMacro(1) 变为 @someMacro(Lit(1))
  • @someMacro("Foo") 变为 @someMacro(Lit("Foo"))

其他一切都照常通过

  • @someMacro(foo) 变为 @someMacro(Term.Name("foo"))
  • @someMacro(Option(2)) 变为 @someMacro(Term.Apply(Term.Name("Option"), Seq(Lit(2))))

这意味着你没有运行时访问这个东西。如果没有 Semantic Api 来解析符号等,您甚至无法正确实例化对象。可能在 scalameta 2 和天堂 4 中是可能的,但现在绝对不可能。您可以做的是进行运行时模式匹配以检查该值。

我在这里做了一些类似的事情(注意这是非常 WIP): https://github.com/DavidDudson/Elysium/blob/master/gen/src/main/scala/nz/daved/elysium/gen/MacroAnnotation.scala

具体见https://github.com/DavidDudson/Elysium/blob/master/gen/src/main/scala/nz/daved/elysium/gen/MacroAnnotation.scala#L149

注意:这意味着在运行时(在该示例中恰好是编译时),如果传入的 arg 类型不正确,则会出现运行时异常

【讨论】:

  • 谢谢@David Dudson 我在编译这个类时遇到错误github.com/DavidDudson/Elysium/blob/master/gen/src/main/scala/… 你能提供天堂_2.11.8-3.0.0-SNAPSHOT.jar 吗? [error] /Users/batemady/tools/scala/samples/meta/scalameta-tutorial/src/main/scala/scala/meta/serialiser/MacroAnnotation.scala:14: ; expected but def found [error] val newStat = q"$compileTimeOnly inline def apply(a: Any): Any = meta { ..$inMetaBlockStats }"
  • 在尝试编译整个 Elysium 项目时出现以下错误[error] Missing required plugin: macroparadise
  • 我终于通过在添加天堂插件的行中添加注释来成功编译
  • 该项目正在使用此 PR github.com/scalameta/paradise/pulls。 Elysium 不应该在没有 PR 的情况下编译,因为它处理生成其他宏的宏
  • 我想做的是在扩展时使用传递给宏的类型参数。在下面的宏中,我可以打印传递给宏的参数,但是在生成返回的树(defn)import scala.annotation.StaticAnnotation class tparam[T, L] extends StaticAnnotation { inline def apply(defn: Any): Any = meta { assert(T.toString == "Int") assert(L.toString == "String") println(T.toString ) println(L.toString) defn } } 时不知道如何使用和替换传入的 T 和 L
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-09-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-16
相关资源
最近更新 更多