Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Construct a List from a series of expressions in Scala

Tags:

scala

dsl

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?

like image 759
Michael Tiller Avatar asked Apr 13 '12 10:04

Michael Tiller


People also ask

How do I create a list in Scala?

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.

How do you create a uniform list in Scala?

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.

What is the difference between SEQ and list in Scala?

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.


2 Answers

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" )
}
like image 140
paradigmatic Avatar answered Oct 05 '22 03:10

paradigmatic


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))))
like image 24
elbowich Avatar answered Oct 05 '22 02:10

elbowich