When I try to build internal DSLs in Scala, I run into a common problem and I haven't been able to craft a solution. To make things look a bit more like a typical language, I'd like the syntax to look something like this:
model 'Foo {
decl 'Real 'x;
decl 'Real 'y;
}
In practice, there are several issues. The first issue is getting a model
object here to take two arguments in this way. If anybody has any ideas, let me know. But what I've done instead is to do something a bit more like this:
model('Foo) {
...
}
Where model is now a function which then returns an object with an apply
method which then consumes the lambda that follows. That I can live with. I could live with a similar issue inside the lambda as well, so things like decl 'Real 'x
or decl('Real,'x)
on the inside. But what I want to do is to get the results of all those expressions inside the squiggly braces to get "returned" as a list. In other words, what I want is to write something like this:
model 'Foo {
decl('Real,'x);
decl('Real,'y);
}
where decl(...)
evaluates to something of type Declaration
and the {...}
then evaluates to List[Declaration]
. I suspect there is some way of using implicits to do this, but I haven't been able to find it. In short, I'd like to make:
model 'Foo {
decl('Real,'x);
decl('Real,'y);
}
...evaluate to the equivalent of...
model 'Foo {
decl('Real,'x) ::
decl('Real,'y) ::
Nil
}
Comments or suggestions?
Syntax for defining a Scala List. val variable_name: List[type] = List(item1, item2, item3) or val variable_name = List(item1, item2, item3) A list in Scala is mostly like a Scala array. However, the Scala List is immutable and represents a linked list data structure.
Creating Uniform Lists in ScalaThe method List. fill() creates a list and fills it with zero or more copies of an element. This fills the list with seven instances of the integer 1.
A Seq is an Iterable that has a defined order of elements. Sequences provide a method apply() for indexing, ranging from 0 up to the length of the sequence. Seq has many subclasses including Queue, Range, List, Stack, and LinkedList. A List is a Seq that is implemented as an immutable linked list.
As a first idea, you could try variable arguments lists, which allows you to use commas instead of semi-colons:
case class Declaration(name: String)
def decl( s: String ) = Declaration(s)
case class Model( sym: Symbol, decls: List[Declaration] )
def model( sym: Symbol)( decls: Declaration* ) =
Model( sym, decls.toList )
val m = model( 'Foo )(
decl( "bar" ),
decl( "baz" )
)
Alternatively, you could extend a trait
to get rid of some parentheses and of the commas:
case class ModelBuilder( sym: Symbol ) {
def using( decls: Declarations ) = Model( sym, decls.toList )
}
trait Declarations {
protected var decls = List[Declaration]()
protected def decl( s: String ) =
decls ::= Declaration( s )
def toList = decls
}
def model( sym: Symbol ) = ModelBuilder( sym )
model( 'Foo ) using new Declarations {
decl( "bar" )
decl( "baz" )
}
Oh god what have I done?
import scala.collection.mutable.ListBuffer
case class Declaration(t: Symbol, name: Symbol)
case class Model(name: Symbol, declarations: List[Declaration])
object model extends Dynamic {
val buffer = ListBuffer.empty[Model]
def applyDynamic(name: String)(args: Any*) {
buffer += Model(Symbol(name), decl.buffer.toList)
decl.buffer.clear()
}
}
object decl extends Dynamic {
val buffer = ListBuffer.empty[Declaration]
def applyDynamic(t: String)(args: Any*) {
args match {
case Seq(name: Symbol) => buffer += Declaration(Symbol(t), name)
}
}
}
model Foo {
decl Real 'x
decl Real 'y
}
assert(model.buffer.head == Model('Foo, List(
Declaration('Real, 'x), Declaration('Real, 'y))))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With