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.
使用引号
希望我能为您提供更清晰的视野和有用的链接,以更深入地探索这些引人入胜的主题。
最近有一次在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.
使用引号希望我能为您提供更清晰的视野和有用的链接,以更深入地探索这些引人入胜的主题。