Summary
The idea here is to use Scala as a Domain Specific Language to create models by connecting actors and setting parameters. A second idea is to use a DSL to define actors themselves.
Simple Example
The Butterfly Example in Ptolemy II
As a proof of concept, we define the Butterfly class in Scala. This class was previously defined in MoML ($PTII/ptolemy/domains/sdf/demo/Butterfly/Butterfly.xml) and Java (ptolemy.domains.sdf.demo.Butterfly.Butterfly.java).
Graph diagram representing a Ptolemy II model corresponding to the Butterfly class.
This graph can be visualized by executing:
$PTII/bin/vergil $PTII/ptolemy/domains/sdf/demo/Butterfly/Butterfly.xml
The Butterfly Example in Scala DSL
Here we compare the Scala code to the existing Java code to define the Butterfly class.
Scala code
| Java code
|
val director = SDFDirector("director").set("iterations", "2400")
|
SDFDirector director = new SDFDirector(this, "director");
setDirector(director);
director.iterations.setExpression("2400");
|
val ramp = Ramp("Ramp").set("step", "PI/100.0")
|
Ramp ramp = new Ramp(this, "Ramp");
ramp.step.setExpression("PI/100.0");
|
val expression = Expression("Expression").set("expression", "-2.0*cos(4.0*ramp) + exp(cos(ramp)) + (sin(ramp/12.0) * (sin(ramp/12.0))^4)")
|
Expression expression = new Expression(this, "Expression");
expression.expression.setExpression("-2.0*cos(4.0*ramp) + exp(cos(ramp)) + (sin(ramp/12.0) *
(sin(ramp/12.0))^4)");
|
expression.ramp = addTypedInputPort(expression, "ramp")
|
TypedIOPort expInput = new TypedIOPort(expression, "ramp");
expInput.setInput(true);
|
val polarToCartesian = PolarToCartesian("Polar to Cartesian")
|
PolarToCartesian polarToCartesian = new PolarToCartesian(this, "Polar to Cartesian");
|
val xyPlotter = XYPlotter("xyPlotter").set("grid", false).set("xRange", (-3, 4)).set("yRange", (-4, 4))
|
XYPlotter xyPlotter = new XYPlotter(this, "xyPlotter");
xyPlotter.plot = new Plot();
xyPlotter.plot.setGrid(false);
xyPlotter.plot.setXRange(-3, 4);
xyPlotter.plot.setYRange(-4, 4);
|
val node = TypedIORelation ("node")
|
TypedIORelation node = (TypedIORelation) newRelation("node");
|
ramp.output --> node --> (expression.ramp, polarToCartesian.angle)
|
ramp.output.link(node);
expInput.link(node);
polarToCartesian.angle.link(node);
|
expression.output --> polarToCartesian.magnitude
polarToCartesian ==> xyPlotter
|
connect(expression.output, polarToCartesian.magnitude);
connect(polarToCartesian.x, xyPlotter.inputX);
connect(polarToCartesian.y, xyPlotter.inputY);
|
Language Features
Connecting Actors
- Infix notation to improve code readabilty:
- actor1.output --> actor2.input instead of connect(actor1.output, actor2.input)
- Implicit port naming. In most cases, when we connect an actor's output port to the input port of an other actor. It is mainly connecting the first output port of an actor's inputPortList to the first output of a second actor's outputPortList. This is how it works when actors ports are not explicitly named. We can write:
- actor1 --> actor2 instead of actor1.output --> actor2.input, if each port is the first in the inputPortList/outputPortList.
- actor1 --> actor2.secondInput instead of actor1.output --> actor2.secondInput, if each secondInput is the second in the inputPortList.
- Chain connections. With implit port naming, it's possible to write chain connections in the same line:
- actor1 --> actor2 --> actor3 instead of actor1.output --> actor2.input; actor2.output --> actor3.input
- Multicast ports connection:
- actor1 --> (actor2, actor3, ..., actorN) instead of actor1 --> actor2; actor1 --> actor3; ...; actor1 --> actorN
- Bus connection. Connect in parallel ports of actor1 to ports of actor2:
- actor1 ==> actor2 instead of actor1.output1 --> actor2.input1; actor1.output2 --> actor2.input2; ...; actor1.outputN --> actor2.inputN;
- Operators (functions) composition for less code:
- actor1 --/4--> actor2 instead of relation = connect(actor1.output, actor2.input); relation.width.setExpression("4")
Dynamic Member
- The PtolemyII DSL allows to access instance members that aren’t statically defined in a class. For example, the Expression actor class doesn't have an input port. To add a port to an Expression instance (named 'expression') in PtolemyII, we need to create an object (named 'port1') of type TypedIOPort contained by the Expression instance. To connect 'expression' to actor 'actor1' we write actor1 --> port1. The issue here is that the programmer has to remember that 'port1' is contained by 'expression'. To improve the conciseness, the languages allows to write 'expression.port1' by adding dynamically (on runtime) the member 'port1' to the instance 'expression'. So we write:
- expression.port1 = addTypedInputPort(expression, "port1")
actor1 --> expression.port1
instead of
TypedIOPort port1 = new TypedIOPort(expression, "port1");
port1.setInput(true);
actor1 --> port1;
Abstraction
- Same way to connect Actors and Relations. In PtolemyII java, we use connect(actor1.output, actor2.input) to connect actor1 to actor2; and actor2.output.link(node) to connect actor2 output port to the relation 'node'. The DSL operator '-->' provides more abstraction:
- actor1.output --> actor2.input
or actor1 --> actor2
- actor2.output --> node
or actor2 --> node
Get the Code
The Scala DSL code is being developed as part of the PtolemyII project under the package org.ptolemy.scala
.
The code is updated on the main repository.
svn co https://repo.eecs.berkeley.edu/svn-anon/projects/eal/ptII/trunk ptII
For more details how to check out the repo, see PtolemyII repo.
Install Eclipse For Scala
It's possible to install Scala IDE http://scala-ide.org/.
But, if you want to keep using your eclipse installation; you need to update eclipse by installing the Scala plugin. The update link is available here.
If the Scala compiler used by Eclipse is not the last release:
- It's recommended to use the latest version of Scala compiler. It can be downloaded from here. And it requires version 8 of the Java platform.
- How to add the new Scala compiler installation to Eclipse can be found here.
How to compile using Eclipse
If the Scala source files not recognized by compiler in Eclipse. Right click on the ptII project, scala -> Add Scala Nature. Then again Right click on the ptII project, scala -> Restart Presentation Compiler.
Since the Scala code is part of the PtolemyII project, there is no additional configuration. Building the project compiles both Java and Scala codes.
How to run using Eclipse
- Run as Scala Application
- In the 'Run Configurations' menu, add new Scala application
- Set the main class as: "org.ptolemy.scala.actor.gui.CompositeActorApplication"
- Set the 'Program arguments' as "-class org.ptolemy.scala.demo.butterfly.Butterfly"
- Set the 'VM arguments' as "-classpath $PTII:$SCALA_LIB/scala-library.jar"
- Apply and Run.
Compile and Run from the Command Line
- Compile from the command line
- cd $PTII/org/ptolemy/scala/
- make
- Run from the command line
- scala -cp $PTII org.ptolemy.scala.actor.gui.CompositeActorApplication -class org.ptolemy.scala.demo.butterfly.Butterfly
Run a Demo from the Command Line
- cd $PTII/org/ptolemy/scala/demo/butterfly
- make demo
Code Generator
The idea here is that we use the org.ptolemy.scala.cg.ActorGenerator
code generator to read in Ptolemy models and generate Scala class files.
For example, to generate Scala class files for Ptolemy models: ptolemy.actor.lib.URLDirectoryReader
and ptolemy.actor.lib.Writer
we run:
scala -cp $PTII org.ptolemy.scala.cg.ActorGenerator ptolemy.actor.lib.URLDirectoryReader ptolemy.actor.lib.Writer
See Also