将Scala String转换为StringContext并实际转发引用

时间:2012-11-07 13:36:25

标签: scala

在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设计,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 “......”),它解析了$ - 引用。但到目前为止我什么也没找到,怎么投这个。简而言之,我如何将"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) = ...
}

1 个答案:

答案 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! :)