使用宏(而不是函数)的优点是Scala?

时间:2015-06-22 16:28:11

标签: scala

Scala中使用的宏是什么?使用函数无法完成的宏可以做些什么?

我想有一个好处是:

  1. 能够在呼叫点解析AST,但这是一个相当罕见的用例。
  2. 代码不会导致额外的函数调用(直接修改AST),但现代编译器非常擅长内联函数。
  3. 我还缺少其他任何优势吗?

2 个答案:

答案 0 :(得分:1)

当它首先阻止你编写代码时,最常见的是宏的优势。

考虑play-json的情况。我可以定义一个case类,play-json格式化程序宏可以创建一些方法,使用我在类中定义的字段将我的CC转换为json和从json转换。

这里的关键是它参与了通常在运行时没有表示的源代码(变量的名称),它对它们进行编译时(类型安全!)反射创建特定的函数而不是使用运行时(不安全)反射。

答案 1 :(得分:1)

我同意Daenyth,宏对代码有一些适度但有用的好处。

为了论证,请考虑play-json 可以生成代码所做的工作:

A)两个哑巴运行:

  1. 将case类定义读入为字符串,并将其写回 更新字符串
  2. 在" real"中使用更新后的定义运行

    一开始非常简单,但很笨重,而且不是类型安全的

  3. B)树和任务:以字符串形式读取def,但使用像Treehugger这样的运行时代码库将代码编写为树,并使用构建工具插件将代码生成任务添加到&#39 ;编译'

    这使我们的类型安全性中途化,使用插件进行顺序编译至少提供了单次运行的错觉。

    C)宏:使用实验性功能在编译时读取和写入树

    宏是完全类型安全的,单一运行,并且在一次编译中完成所有操作意味着轻松修改生成的代码

    例如

    假设我使用代码生成库将def printType添加到case class Record(x: Int),并提供

    case class Record(x: Int) {
      def printType = println("Int")
    }
    

    现在说我想将自己的def goodbye添加到课程中:

    没有宏: 我可以

    1)尝试将输出修改为

    case class Record(x: Int) {
      def printType = println("Int")
      def goodbye = println("bye")
    }
    

    然后我遇到输出文件顶部打印的this is a generated file, DO NOT EDIT,提醒我文件将被覆盖,所以我要么不得不去解决代码生成的麻烦,或

    2)尝试修改输入     case class Record(x:Int){       def goodbye = println(" bye")     } 但是代码库可能不会看到任意代码,所以我必须修改代码库本身。

    使用宏: 如果代码库依赖于宏(并且库没有明确地处理类主体),我可以添加我的新的def输入

    case class Record(x: Int) {
      def goodbye = println("bye")
    }
    

    它只是有效;我的def在那里,生成的def也在那里。

    case class Record(x: Int) {
      def printType = println("Int")
      def goodbye = println("bye")
    }