Scala3:内联与引用(宏)
最近我有一个在Scala 3中编写宏的经验。我用于inline简单的函数和引用代码,scala.quoted用于更复杂的操作。
似乎这两个功能都用编译时生成的代码替换了一些运行时代码,但inline有一些限制。
它们之间有什么区别,为什么inline不能到处使用引用代码代替?
回答
TL;DR inline是一种在编译时用完整的函数体替换函数调用的机制(一种优化形式)。在 Scala 中,它还用于编写宏,即将在编译时评估的函数,可以操作 AST Scala 代码。
scala.quoted包含使用 quasiquoting(一种简洁的符号,让您可以轻松操作 Scala 语法树)和拼接(quasiquoting 的逆运算符)运算符编写宏的函数。通常,它们一起用于创建编译时元编程。事实上,它inline是一种启用机制,并scala.quoted提供了一些操作和评估 Scala AST 的功能。当你不能在编译时推断出某些东西(或者你想在运行时生成代码)时,你不能使用内联,而应该单独使用 scala.quoted。
内联是一种用于内联代码而不是执行函数调用的机制。因此,例如:
inline def foo : Int = 3
foo
变成了这样
inline def foo : Int = 3
3
其他语言(如 Kotlin)也引入了这种机制,但在 Scala 语言中还有另一个相关特性:在内联扩展期间,编译器可以执行进一步的编译时操作来操纵内联输出过程。可以使用内联条件和内联匹配来执行一种编译元编程形式:
inline acceptString(value : String) : Boolean = inline match {
case "name" => true
case "other" => false
case _ => error("error thrown at compile time")
}
"hello"例如,如果您作为参数 a 传递,则此代码会引发异常(在编译时)。
而是scala.qouted包含操作 Scala AST 的操作符:引用(Quasiquotes 是一种简洁的表示法,可以让您轻松操作 Scala 语法树:)和拼接(quasiqoute 的逆操作符)。理论上,这些运算符可以在运行时(使用新的 TASTy 结构)和编译时(使用inline)使用。如前所述这里,内联和报价之间的关系是:
就其本身而言,原则性元编程看起来更像是运行时元编程的框架,而不是使用宏进行编译时元编程的框架。但是结合 Scala 3 的内联功能,它可以变成一个编译时系统。这个想法是,宏细化可以理解为宏库和引用程序的组合。
有时,您被迫使用运行时扩展,因为某些信息无法在编译时推断出来,或者因为您想在运行时生成代码。因此,您可以对Runtime Multi-Stage Programming和TASTy Inspection使用引用。
我希望我能给你一个更清晰的愿景和有用的链接,以更深入地探索这些引人入胜的话题。