2

In Scala 2.10.0-RC1 there's a wonderful feature, the String Interpolation.

This works like this:

scala> val x = 1
x: Int = 1

scala> val y = s"Interpolated String $x"
y: String = Interpolated String 1

But I'm building a internal DSL with Scala, and therefore I need to handle with a sort of forward references. That you have in mind, what the DSL looks like (it's for documentation generation):

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"
  )
}

But the problem looks semantically like this:

object X {
  val y = s"$x with reverse reference"
  val x = 3
}

And this doesn't work, because of the virtually forward reference. I can write x as lazy val, but the compiler moves x and makes it for y available. But for my DSL design the order of y and x must be preserved!

So far my solution is a closure, like this:

object X {
  val y = () => s"$x with reverse reference"
  val x = 3
}

This allows me to invoke X.y() at the end, when all variables are available. But the disadvantage is, the "ugliness", no domain-user will understand this kind of magic! The domain user must write something like this (but it works!):

++ 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.
""")

So the idea is to move the closure magic into my DSL api, but therefore I need to pass "$x with reverse reference" as ordinary String and resolve this String at the end as StringContext (s"..."), which resolves the $-references. But so far I found nothing, how to cast this. In short how can I cast "foo $x bar to s"foo $x bar"?

What I found is, how to individualize StringContext, but don't know how I can use this for my problem:

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

The other idea is a less "noisy" lambda notation, but how can I write something like (or similar) this:

++ txt => s"""... ${Main.fig.number} ..."""

Such a notation would be reasonable. I don't necessarily to write my own String parser with reference resolving. Anybody an sneaky idea?

The DSL api is semantically built like this:

object ++ {
  def txt (s: String) = ...
}
7
  • what if you define y as lazy? lazy val y = s"$x with reverse reference" Commented Nov 7, 2012 at 13:42
  • "I can write x as lazy val, but the compiler moves x and makes it for y available. But for my DSL design the order of y and x must be preserved!" Commented Nov 7, 2012 at 13:43
  • Sorry, I didn't get that you meant ordering in the bytecode Commented Nov 7, 2012 at 13:48
  • "I can write x as lazy val" Write y as lazy val as @pagoda_5b said. Commented Nov 7, 2012 at 16:28
  • This will also confuse the right order. There is a document builded which relies heavily on the order. The classes which are called by § or ++ register them self into a list, and this must happen in the right order. Don't focus so much on this lazy-thing, especially because I want to avoid to much magic at the "frontend" for the domain users. :) Commented Nov 7, 2012 at 16:52

1 Answer 1

3

With the idea and solution from this question, a possible solution looks like this (run-able on 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()

Thanks for that, Rex Kerr! :)

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.