Comparing Scala to Java in PhET Simulations

Sam Reid, 3-5-2009

I've been using Scala for 80+ hours in development of PhET simulations (after using Java for 8+ years), and have found there to be several advantages in the language over Java.
However the benefits need to be significant because Scala currently brings several disadvantages:
Furthermore, Scala is no silver bullet; it doesn't make domain problems such as physics, biology or mathematics inherently simpler, and it doesn't alleviate the need for good software engineering principles.
However, it does make it easier to represent many domain problems in software, and it makes it easier to write better (more concise, elegant and maintainable) software.

So let's discuss language features in Scala that have made my interactive simulation development easier so far.
Many of the examples below are taken from the PhET codebase, with Java code and Scala code that basically fulfill the same role, or Scala snippets that demonstrate a point.
If the examples below were the only instances of Scala improvements over Java for our project, then the switch to Java wouldn't be justified at this time;
however, these patterns occur over and over throughout our simulations and supporting codebase.

Also, some of the Java boilerplate code below can be produced by the IDE; this is fine during the first pass of writing the software,
but requires more work at debug/maintenance time.

Please note, the examples below are meant more as slides in a talk than as a stand-alone document.

Observable Pattern

From Java/glaciers/TestViewport.

_square.addListener( new SquareListener() {
    public void positionChanged() {
        updatePosition();
    }
});

From Scala/ladybug2d.

model.maze.addListenerByName( updateTransform() )

View-Controllers

From Java/Rotation/AngleUnitsSelectionControl (see also ph-scale/ScaleControlPanel line 60)

 degrees = new JRadioButton( RotationStrings.getString( "units.degrees" ) );
 degrees.addActionListener( new ActionListener() {
     public void actionPerformed( ActionEvent e ) {
         angleUnitModel.setRadians( false );
     }
 } );
 add( degrees );

 angleUnitModel.addListener( new AngleUnitModel.Listener() {
     public void changed() {
         update();
     }
 } );
 update();

 private void update() {
     degrees.setSelected( !angleUnitModel.isRadians() );
 ...
 }

From Scala/RemoteControl line 182

add(new MyRadioButton("Position", mode = positionMode, mode == positionMode, this.addListener))
def mode_=(m: RemoteMode) = {
    _mode = m
    notifyListeners
}

Scala library class that makes this possible

class MyRadioButton(text: String, actionListener: => Unit,
                    getter: => Boolean,
                    addListener: (() => Unit) => Unit)
    extends RadioButton(text) {...}

Math/Physics

example of existing code: Conductivity/ArrowShape line 45.

AbstractVector2D phetvector = direction.getScaledInstance( d ).getAddedInstance( norm.getScaledInstance( d1 ) );
return tipLocation.getAddedInstance( phetvector );

Mock-up Java version.

model.ladybug.setAcceleration((getVelocity(t + dt).minus(getVelocity(t - dt))).dividedBy(dt));

scala, LadybugMotionModel line 148

model.ladybug.setAcceleration((getVelocity(t + dt) - getVelocity(t - dt)) / dt)

Control Abstractions

Doing this the usual way, we are prone to forget: (a) to call the first update or (b) to attach listener to the model.

m.addListenerByName(update())
update
def update()= {
    text.setText(new DecimalFormat("0.00").format(m.getTime)+" sec")
}

Scala implementation. Using a control structure ensures everything will happen, and is more concise

val update = defineInvokeAndPass(model.addListenerByName){
    text.setText(new DecimalFormat("0.00").format(model.getTime) + " sec")
}

Scala library implementation

def defineInvokeAndPass(m: (=> Unit) => Unit)(block: => Unit): () => Unit = {
    block
    m(block)
    block _
}

Implicit Conversions

Java implementation.

def crossed(line: Line2D.Double, start: Vector2D, end: Vector2D) = {
    val intersection = MathUtil.getLineSegmentsIntersection(line, new Line2D.Double(toPoint2D(start),toPoint2D(end))
}

Scala implementation

def crossed(line: Line2D.Double, start: Vector2D, end: Vector2D) = {
    val intersection = MathUtil.getLineSegmentsIntersection(line, new Line2D.Double(start, end))
}

Scala library implementation

implicit def vector2DToPoint(vector: Vector2D) = new Point2D.Double(vector.x, vector.y) 

Local functions

Local functions avoid namespace pollution and allow you to put the relevant function definition right next to its usage.

Scala implementation

    def test()={
        ...
        def tx(pt: Point2D) = {
          val intermediate = getWorldTransformStrategy.getTransform.inverseTransform(pt, null)
          val model = transform.viewToModel(intermediate.getX, intermediate.getY)
          model
        }
        val out = new Rectangle2D.Double()
        out.setFrameFromDiagonal(tx(topLeft).getX, tx(topLeft).getY, tx(bottomRight).getX, tx(bottomRight).getY)
    }

Generics as in Java 5

I've been using generics even though we are running on 1.4 JRE.

Uniform object model

No primitives, no autoboxing, no statics; everything is an object. 1+2 is the same as 1.+(2)

Better support for singleton behavior

Singletons can subclass/override.

Better ternary operator

Java ternary operator

module.model.isPaused?"Play":"Pause"

Scala if statement, see LadybugClockControlPanel

if (module.model.isPaused) "Play" else "Pause"

For loops have a value

Scala implementation in LadybugModel

    val tx = for (item <- h) yield new TimeData(item.state.position.x, item.time)
    val vx = MotionMath.estimateDerivative(tx.toArray)

Collections + Function Literals

Scala implementation in AphidMazeModel

aphids.foreach(handleCollision(_))

Case classes

Scala implementation in DataPoint

case class DataPoint(time: Double, state: LadybugState)

Swing Wrapper

new MyRadioButton("Record", model.setRecord(true), model.isRecord, model.addListener) {
  font = new PhetFont(15, true)
}

Tuples

Atomic Casting

There are 786 casts in simulations-java/common, and 186 in glaciers. Many of these casts take the form as in this example:
if ( gridStrategy instanceof GridStrategy.Relative ) {
    ( (GridStrategy.Relative) gridStrategy ).setSpacing( spacing );
}

In Scala, atomic casts can be done by pattern matching

myComponent match{
    case jc:JComponent => jc.paintImmediately(0,0,jc.getWidth,jc.getHeight)
    case _ => Unit
}