在Scala 2.10.0-RC1中有一个很棒的功能, String Interpolation 。
这样的工作原理如下:
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 “......”),它解析了$
- 引用。但到目前为止我什么也没找到,怎么投这个。简而言之,我如何将"foo $x bar
投射到s"foo $x bar"
?
我found是什么,如何个性化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} ..."""
这样的符号是合理的。我不一定要用引用解析来编写自己的String解析器。有人偷偷摸摸的想法吗?
DSL api的语义构建如下:
object ++ {
def txt (s: String) = ...
}
答案 0 :(得分:3)
根据this question的想法和解决方案,可能的解决方案如下所示(可在 Scala 2.10.0-RC2 上运行):
implicit def byname_to_noarg[A](a: => A) = () => a
case class StringContext(parts: String*) {
def $ (args: (() => Any)*) = () => {
val unpacked = args.map(a => a())
scala.StringContext(parts: _*).s(unpacked: _*)
}
}
class A {
def meth = "A's result"
}
object X {
val y = $"foo $x bar ${z.meth}. Forward reference solved."
val x = 3
val z = new A
}
// Execute
X.y()
谢谢你,Rex Kerr! :)