在 Scala 2.10.0-RC1 中有一个很棒的特性,字符串插值。
这像这样工作:
scala> val x = 1
x: Int = 1
scala> val y = s"Interpolated String $x"
y: String = Interpolated String 1
但是我正在用 Scala 构建一个内部 DSL,因此我需要处理一种前向引用。您已经想到了 DSL 的样子(用于文档生成):
object Main {
§ > "Heading"
§ >> "Subheading"
++ txt """
Lorem ipsum Abb. ${Main.fig.number} dolor sit amet, consetetur sadipscing elitr, sed diam
nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat,
sed diam voluptua.
"""
val fig =
++ figure(
src="https://...",
desc="example figure"
)
}
但是这个问题在语义上看起来像这样:
object X {
val y = s"$x with reverse reference"
val x = 3
}
这不起作用,因为实际上是前向引用。我可以写x
为lazy val
,但编译器会移动x
并使其y
可用。但是对于我的 DSL 设计y
,x
必须保留顺序!
到目前为止,我的解决方案是一个闭包,如下所示:
object X {
val y = () => s"$x with reverse reference"
val x = 3
}
X.y()
当所有变量都可用时,这允许我在最后调用。但缺点是,“丑”,没有域用户会懂这种魔法!域用户必须写这样的东西(但它有效!):
++ txt (() => s"""
Lorem ipsum Abb. ${Main.fig.number} dolor sit amet, consetetur sadipscing elitr, sed diam
nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat,
sed diam voluptua.
""")
所以这个想法是将闭包魔法移动到我的 DSL api 中,但因此我需要"$x with reverse reference"
像普通一样传递String
并将这个字符串最后解析为StringContext
(s"..."),它解析了$
-references。但到目前为止,我什么也没找到,如何投射这个。总之我怎么能"foo $x bar
投到s"foo $x bar"
?
我发现的是,如何个性化StringContext
,但不知道如何使用它来解决我的问题:
scala> case class StringContext(parts: String*) {
| def rr (args: Any*) = scala.StringContext(parts: _*).s(args: _*)
| }
defined class StringContext
scala> rr"hi"
res3: String = hi
scala> rr"hi $s"
res4: String = hi 1
另一个想法是不那么“嘈杂”的 lambda 表示法,但我怎样才能写出类似(或类似)的东西:
++ txt => s"""... ${Main.fig.number} ..."""
这样的符号是合理的。我不一定要编写自己的带有引用解析的字符串解析器。有人有什么鬼主意吗?
DSL api 在语义上是这样构建的:
object ++ {
def txt (s: String) = ...
}