Scala宏,它们在哪里使用?

Scala宏,它们在哪里使用?,scala,macros,metaprogramming,template-meta-programming,Scala,Macros,Metaprogramming,Template Meta Programming,我刚刚注意到Scala有宏,但我从未见过任何使用宏的代码。它们似乎也与C预处理器宏等有很大不同。通读宏的历史,看起来它们并没有提供任何以前在Scala中不可能提供的功能。在动机标题下,有一个宏启用的事项列表: 语言虚拟化(重载/重写 支持DSL深度嵌入的原始编程语言) 程序具体化(为程序提供检查其性能的方法 自己的代码) 自我优化(特定领域的自我应用) 基于程序具体化的优化) 算法程序 构造(生成代码,用它编写代码很枯燥) 由编程语言支持的抽象) 在菜单的后面,有实验性的宏功能,如类型宏、准

我刚刚注意到Scala有宏,但我从未见过任何使用宏的代码。它们似乎也与C预处理器宏等有很大不同。通读宏的历史,看起来它们并没有提供任何以前在Scala中不可能提供的功能。在动机标题下,有一个宏启用的事项列表:

  • 语言虚拟化(重载/重写 支持DSL深度嵌入的原始编程语言)
  • 程序具体化(为程序提供检查其性能的方法 自己的代码)
  • 自我优化(特定领域的自我应用) 基于程序具体化的优化)
  • 算法程序 构造(生成代码,用它编写代码很枯燥) 由编程语言支持的抽象)
在菜单的后面,有实验性的宏功能,如类型宏、准类型宏、非类型宏等等。显然有这样的需求

对于那些对Scala有着深刻理解的构建非常复杂的库的人来说,所有这些似乎都是不错的特性。但是宏是否也为普通的Scala开发人员提供了一些东西呢?使用宏会使我的Scala代码更好吗?

作为一个“普通”的Scala开发人员,除非你有很好的理由,否则你很可能不会自己编写宏

宏是编译时元编程的一种方法,也就是说,您可以编写程序。例如,作为Scala 2.10的一部分的def宏(尽管仍然是“实验性的”)看起来像一个常规方法,但每当您在代码中调用该方法时,编译器将用隐藏在该方法后面的任何宏(新的代码片段)替换该调用


一个非常简单的例子。将项目编译到代码中的日期包括在内:

import java.util.Date
import reflect.macros.Context
import language.experimental.macros

object CompileTime {
  def apply(): Date = macro applyImpl

  def applyImpl(c: Context)(): c.Expr[Date] = {
    import c.universe._
    val now     = System.currentTimeMillis() // this is executed during compilation!
    val nowExpr = c.Expr[Long](Literal(Constant(now)))
    val code    = reify(new Date(nowExpr.splice))
    c.Expr(code.tree)
  }
}
使用该宏(以下代码必须与上述宏代码分开编译):

(如果运行多次,您会发现编译时间确实是恒定的)


因此,简而言之,宏提供了在任何以前的Scala版本中都不可用的功能。您可以使用宏执行其他操作无法执行的操作(通常可以使用运行时反射编写类似的操作,但宏是在编译时检查的)

但是,作为用户,您将越来越多地接触到包含宏的库,因为它们可以提供完全类型安全的强大构造。例如,可以使用宏实现case类中JSON的自动序列化,因为宏可以检查case类的类型,并构建正确的程序结构(AST)来读写该case类,而不会有运行时失败的危险

一些随机链接


许多日志和序列化库在幕后使用宏。您可以看看我们最新的论文,其中概述了宏的一些有趣用法:。下面是一篇专门的文档站点文章:
object MacroTest extends App {
  println(s"This project was compiled on ${CompileTime()}")
}