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
}

そして、これは事実上前方参照のために機能しません。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

もう1つのアイデアは、「ノイズの少ない」ラムダ表記ですが、次のような(または同様の)ものをどのように記述できますか。

++ 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 に答える