Consolidated older post - originally posted in Aug, 2011
When coming up with a scala internal DSL, it is often that one will come up with constructs that produce things that need collected, in a list, tree or similar.
For instance, instructions for a robot:
$if (condition) {
move(1) :: move(2) :: move(3) :: Nil
move(1) + move(2) + move(3)
}
Due to syntax restrictions, it is often that people use operators to chain these, like the overloaded + above.
There is a technique you can use to make this look more familiar:
$if (condition) {
move(1)
move(2)
move(3)
}
While not the nicest, the idea is to use a static collector and collect the actions in there. You'll need levels, so that collecting nested blocks don't interfere, so there is a stack of collectors. Also, these collectors need to be thread-local so that different collecting threads don't interfere.
A reusable collector is in DslCollector.scala and a sample in DslCollectorTest.scala.
For this pattern, you have a Collector, a Collectable and the actual constructs:
case class move(i: Int) extends DslCollectable
def test1 = expect(move(1) :: move(2) :: Nil) {
val collected = new collection.mutable.ListBuffer[Any]()
DslCollector.collect { collected += _ }(1) { // start a collector level
move(1) //collects itself
move(2) //collects itself
} // collector ends
collected
}
You can then embedd this in a higher-level construct of your DSL, like the $if used in the example, see this and this for the concrete code.
Enjoy!