2

在 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
}

这不起作用,因为实际上是前向引用。我可以写xlazy val,但编译器会移动x并使其y可用。但是对于我的 DSL 设计yx 必须保留顺序!

到目前为止,我的解决方案是一个闭包,如下所示:

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) = ...
}
4

1 回答 1

3

有了这个问题的想法和解决方案,一个可能的解决方案看起来像这样(可在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()

谢谢你,雷克斯科尔!:)

于 2012-11-09T13:30:28.987 回答