Scala 3:内联与引用(宏)

Scala 3: inline vs quoted (macros)

最近有一次在Scala 3中写宏的经验。我用inline来实现简单的功能,用scala.quoted引用代码来实现更复杂的操作。

似乎这两个功能都用编译时生成的代码替换了一些运行时代码,但是inline有一些限制。

它们之间的区别是什么?为什么 inline 不能在所有地方使用而不是引用代码?

TL;DR inline 是一种在编译时用其完整主体替换函数调用的机制(一种优化形式)。在 Scala 中,它还用于编写宏,即将在编译时评估的函数,可以操作 AST Scala 代码。 scala.quoted 包含使用准引用(一种简洁的符号,可让您轻松操作 Scala 语法树)和拼接(准引用的逆运算符)运算符编写宏的函数。通常,它们一起用于创建编译时元编程。 实际上,inline 是启用机制,scala.quoted 提供了一些操作和评估 Scala AST 的功能。当你不能在编译时推断出某些东西时(或者你想在运行时生成代码),你不能使用内联,你应该单独使用 scala.quoted。

内联是一种用于内联代码而不是执行函数调用的机制。 因此,例如:

inline def foo : Int = 3
foo

变成了这个样子

inline def foo : Int = 3
3

其他语言(如Kotlin)也引入了这种机制,但在Scala语言中还有一个相关的特点:在inline expansion过程中,编译器可以进行进一步的编译时操作来操纵inline输出过程。 可以使用内联条件和内联匹配执行一种编译元编程形式:

inline acceptString(value : String) : Boolean = inline match {
    case "name" => true
    case "other" => false
    case _ => error("error thrown at compile time")
}

如果您将 "hello" 作为参数传递,此代码将抛出异常(在编译时)。

相反 scala.qouted 包含操作 Scala AST 的运算符:引用(Quasiquotes 是一种简洁的符号,可让您轻松操作 Scala 语法树:) 和拼接(quasiqoute 的逆运算符)。理论上,这些运算符可以在运行时(使用新的 TASTy 结构)和编译时(使用 inline)使用。 如here所述,内联和引用之间的关系是:

Seen by itself, principled metaprogramming looks more like a framework for runtime metaprogramming than one for compile-time metaprogramming with macros. But combined with Scala 3’s inline feature it can be turned into a compile-time system. The idea is that macro elaboration can be understood as a combination of a macro library and a quoted program.

有时,您不得不使用运行时扩展,因为某些信息无法在编译时推断出来,或者因为您想在运行时生成代码。所以你可以对 Runtime Multi-Stage Programming and TASTy Inspection.

使用引号

希望我能为您提供更清晰的视野和有用的链接,以更深入地探索这些引人入胜的主题。