标签: scala-macros

从方法符号和正文创建方法定义树

有没有一种方便的方法可以在Scala 2.10中将MethodSymbol方法定义树(即a DefDef)的左侧转换为?

例如,假设我想创建一个宏来获取特征的实例,并用一些调试功能包装所有特征的方法.我可以写下面的内容:

import scala.language.experimental.macros
import scala.reflect.macros.Context

object WrapperExample {
  def wrap[A](a: A): A = macro wrap_impl[A]

  def wrap_impl[A: c.WeakTypeTag](c: Context)(a: c.Expr[A]) = {
    import c.universe._

    val wrapped = weakTypeOf[A]
    val f = Select(reify(Predef).tree, "println")

    val methods = wrapped.declarations.collect {
      case m: MethodSymbol if !m.isConstructor => DefDef(
        Modifiers(Flag.OVERRIDE),
        m.name,
        Nil, Nil,
        TypeTree(),
        Block(
          Apply(f, c.literal("Calling: " + m.name.decoded).tree :: Nil),
          Select(a.tree, m.name)
        )
      )
    }.toList

  //...
}
Run Code Online (Sandbox Code Playgroud)

我已经省去了无聊的业务,将这些方法粘贴在一个新的匿名类中,该类实现了特性,然后实例化了该类 - 如果你感兴趣的话,你可以在这里找到一个完整的工作示例.

现在我可以写这个,例如:

scala> trait X …
Run Code Online (Sandbox Code Playgroud)

reflection macros scala scala-2.10 scala-macros

8
推荐指数
1
解决办法
1056
查看次数

如何区分编译器推断的隐式转换与显式调用的转换?

让我们假设将这两个等价表达式传递给Scala宏:

  • 使用编译器推断的隐式转换: 1+"foo"
  • 使用显式调用的隐式转换: any2stringadd(1)+"foo"

有没有办法在宏内区分这两个?

scala implicit-conversion scala-2.10 scala-macros

8
推荐指数
1
解决办法
288
查看次数

多参数和参数列表的准引号

Quasiquotes非常棒 - 它们使得Scala中的宏写得非常痛苦,而且根据我的经验,它们几乎总是按照我的预期完成工作.最重要的是,它们现在可以作为 Scala 2.10中的插件使用.

这个问题是关于我在写这篇博文时遇到的一个小问题.当我找到几分钟时,我会在我的列表中查看,但我想我会在这里发布它以防万一其他人可以打败我,并帮助其他人遇到同样的问题.

假设我有一个名称类型对的列表列表:

val pss = List(
  List(newTermName("x") -> typeOf[Int], newTermName("y") -> typeOf[Char]),
  List(newTermName("z") -> typeOf[String])
)
Run Code Online (Sandbox Code Playgroud)

我想把它们变成一棵看起来像这样的树:

def foo(x: Int, y: Char)(z: String) = ???
Run Code Online (Sandbox Code Playgroud)

以下工作正常:

q"def bar(${pss.head.head._1}: ${pss.head.head._2}) = ???"
Run Code Online (Sandbox Code Playgroud)

也就是说,它构建了以下树:

def bar(x: Int) = ???
Run Code Online (Sandbox Code Playgroud)

这表明我应该能够写出这样的东西:

val quoted = pss.map(_.map { case (n, t) => q"$n: $t" })

q"def foo..${quoted.map(ps => q"($ps)")} = 1"
Run Code Online (Sandbox Code Playgroud)

或者更简单,在单个参数列表中有多个参数:

q"def baz(..${quoted.head}) = ???"
Run Code Online (Sandbox Code Playgroud)

两者都不起作用 - 我得到这样的错误:

<console>:28: error: type mismatch;
 found …
Run Code Online (Sandbox Code Playgroud)

macros scala scala-macros scala-quasiquotes

8
推荐指数
1
解决办法
1315
查看次数

在Scala宏中匹配XML文字

我想用宏转换Scala XML文字.(不是带XML的字符串文字,而是实际的XML文字).据我所知,XML文字实际上并没有构建在AST级别的语言中,而是在解析器中被贬低.有趣的是,这确实有效:

case q"<specificTag></specificTag>" => ... // succeeds for specificTag with no
                                           // attributes and children
Run Code Online (Sandbox Code Playgroud)

但显然,这完全没用,因为不可能以任意方式匹配任意xml.就像是

case q"<$prefix:$label ..$attrs>$children</$prefix:$label>" => ...
Run Code Online (Sandbox Code Playgroud)

无法工作,因为我们必须在模式中绑定相同的变量两次.

打印出这样的xml文字表达式的树实际上给出了desugared版本.例如.

new _root_.scala.xml.Elem(null,"specificTag",_root_.scala.xml.Null,$scope,false)
Run Code Online (Sandbox Code Playgroud)

但尝试匹配此失败:

case q"new _root_.scala.xml.Elem(..$params)" => ... // never succeeds
Run Code Online (Sandbox Code Playgroud)

我很迷惑!我的问题是:有没有办法可靠地匹配scala宏中的任意xml litarals?另外:为什么它们在quasiquotes中支持常量xml而不是desugared值呢?

scala scala-xml scala-macros scala-quasiquotes

8
推荐指数
1
解决办法
481
查看次数

在Play Json库中为单例提供隐含值

我有以下配置:

sealed trait Status
case object Edited extends Status
case object NotEdited extends Status

case class Tweet(content:String, status:Status)
Run Code Online (Sandbox Code Playgroud)

我想使用Play Json格式,所以我想我必须有这样的东西(我不想在伴侣对象中这样做):

trait JsonImpl{
    implicit val TweetFormat = Json.format[Tweet]
    implicit val statusFormat = Json.format[Status]
    implicit val StatusFormat = Json.format[Edited.type]
    implicit val NotEditedFormat = Json.format[NotEdited.type]
}
Run Code Online (Sandbox Code Playgroud)

但编译器抱怨并说:

No implicit format for Tweet available.

它也说我无法使用,Edited.type因为它需要应用和取消应用功能.我该怎么办?

EDIT1:

我能想到这样的事情:

implicit object StatusFormat extends Format[Status] {
    def reads(json: JsValue) =
      (json \ "type").get.as[String] match {
        case "" => Edited
        case _ => UnEdited
      }

    def …
Run Code Online (Sandbox Code Playgroud)

json scala playframework scala-macros

8
推荐指数
2
解决办法
716
查看次数

使用Scala宏来生成方法

我想在Scala 2.11+中使用注释宏生成方法的别名.我甚至不确定是否可能.如果有,怎么样?

示例 - 如下所示,我希望注释宏扩展为

class Socket {
  @alias(aliases = Seq("!", "ask", "read"))
  def load(n: Int): Seq[Byte] = {/* impl */}
}
Run Code Online (Sandbox Code Playgroud)

我想要上面生成同义词方法存根,如下所示:

class Socket {
  def load(n: Int): Seq[Byte] = // .... 
  def !(n: Int) = load(n)
  def ask(n: Int) = load(n)
  def read(n: Int) = load(n)
}
Run Code Online (Sandbox Code Playgroud)

以上当然是一个诙谐的例子,但我可以看到这种技术对于自动生成API的同步/异步版本或在具有大量同义词的DSL中很有用.是否也可以在Scaladoc中公开这些生成的方法?这可能是使用Scala meta吗?

注意:我要问的是:https://github.com/ktoso/scala-macro-method-alias

也请不要纪念这个作为重复这个因为这个问题是有点不同,很多斯卡拉宏观土地已经改变了过去3年.

macros scala scala-macros scala-macro-paradise scalameta

8
推荐指数
1
解决办法
1544
查看次数

Gradle Scala插件是sbt中addCompilerPlugin的推论

将Scala编译器插件添加到Gradle中的scalaCompile任务的最佳方法是什么?

scala gradle scala-macros

8
推荐指数
1
解决办法
823
查看次数

在sbt中解决jar加载冲突

我在sbt启动时遇到以下错误,当两个特定的sbt插件一起添加到其构建定义中的项目时.其中一个sbt插件是scalikejdbc,另一个是我自己的,显然它们在项目的构建定义中的相互包含导致sbt启动时出现此错误:

scala.reflect.internal.Types$TypeError: package macros contains object
and package with same name: blackbox
Run Code Online (Sandbox Code Playgroud)

显然,看起来每个插件都带有不同版本的scala.reflect.macros,从而导致此错误.我可以在sbt插件的罐子里戳,其中其中一个似乎带来了scala/reflect/macros/blackbox.class-

$ jar -tvf scalikejdbc-core_2.10/jars/scalikejdbc-core_2.10-2.4.2.jar | grep black

  0 Sat Jun 11 15:51:10 IDT 2016 scala/reflect/macros/blackbox/    
405 Sat Jun 11 15:51:04 IDT 2016 scala/reflect/macros/blackbox/package$.class    
419 Sat Jun 11 15:51:04 IDT 2016 scala/reflect/macros/blackbox/package.class
Run Code Online (Sandbox Code Playgroud)

- 但是,在这个早期启动阶段很难正确地得出sbt加载的内容,以便确定这个包的哪个版本在这里涉及以及哪些依赖关系带来了它们.

感谢您关于如何进行此调查的建议!


并且为了完整起见 - 我在下面仅仅包括sbt给出的完整错误 - 但我真诚地怀疑它添加了任何信息,所以你可能会忽略它.

scala.reflect.internal.Types$TypeError: package macros contains object and package with same name: blackbox
one of them needs to be removed …
Run Code Online (Sandbox Code Playgroud)

scala sbt scala-macros scalikejdbc sbt-plugin

8
推荐指数
1
解决办法
942
查看次数

Scala 3 (Dotty) 模式匹配带有宏引用的函数

我正在尝试通过 Scala 3.0.0-M2 中的宏获取函数名称我提出的解决方案使用 TreeAccumulator

import scala.quoted._

inline def getName[T](inline f: T => Any): String = ${getNameImpl('f)}

def getNameImpl[T](f: Expr[T => Any])(using Quotes): Expr[String] = {
  import quotes.reflect._
  val acc = new TreeAccumulator[String] {
    def foldTree(names: String, tree: Tree)(owner: Symbol): String = tree match {
      case Select(_, name) => name
      case _ => foldOverTree(names, tree)(owner)
    }
  }
  val fieldName = acc.foldTree(null, Term.of(f))(Symbol.spliceOwner)
  Expr(fieldName)
}
Run Code Online (Sandbox Code Playgroud)

调用此代码时会生成函数的名称:

case class B(field1: String)
println(getName[B](_.field1)) // "field1"
Run Code Online (Sandbox Code Playgroud)

我想知道这是否可以使用引号以更简单的方式完成。

macros scala dotty scala-macros scala-3

8
推荐指数
1
解决办法
290
查看次数

在多阶段编译中,我们是否应该使用标准的序列化方法来通过阶段传送对象?

这个问题是在 Scala 3/Dotty 中提出的,但应该推广到 MetaML 系列之外的任何语言。

\n

Scala 3 宏教程:

\n

https://docs.scala-lang.org/scala3/reference/metaprogramming/macros.html

\n

从阶段一致性原则开始,它明确指出在编译阶段定义的自由变量不能被下一阶段使用,因为它的绑定对象不能持久化到不同的编译器进程:

\n
\n

...因此,程序的结果将需要将程序状态本身作为其部分之一进行持久化。我们不想\xe2\x80\x99 这样做,因此这种情况应该被定为非法

\n
\n

这应该被认为是一个已解决的问题,因为许多分布式计算框架需要类似的能力来跨多台计算机持久保存对象,最常见的解决方案(如在 Apache Spark 中观察到的)使用标准序列化/pickling 来创建绑定对象的快照( Java 标准序列化,twitter Kryo/Chill)可以保存在磁盘/堆外内存上或通过网络发送。

\n

教程本身也两次提出了这种可能性:

\n
\n

一个区别是 MetaML 没有 PCP 的等效项 - MetaML 中引用的代码可以访问其直接封闭环境中的变量,但有一些限制和警告,因为此类访问涉及序列化。然而,这并不构成表现力的根本增益。

\n
\n
\n

最后,ToExpr非常类似于序列化框架

\n
\n

相反,Scala 2 和 Scala 3(以及它们各自的生态系统)在很大程度上忽略了这些开箱即用的解决方案,只为原始类型提供默认方法(Liftable在 scala2 中,ToExpr在 scala3 中)。此外,使用宏的现有库在很大程度上依赖于手动定义准引号/引号来完成这项琐碎的任务,从而使源代码变得更长且更难以维护,同时并没有使任何东西变得更快(因为 JVM 对象序列化是高度优化的语言组件)

\n

造成这种现状的原因是什么?我们如何改进它?

\n

scala metaprogramming multistage scala-macros scala-3

8
推荐指数
0
解决办法
162
查看次数