User Tools

Site Tools


Sidebar

Dave Orme muses about agile and functional programming.

My current work emphasizes SOA applications using Scala, Kubernetes, and AWS with a React-based SPA front-end. I'm also interested in progressive web applications and developer tools.


Blog

Scala, Clojure, and FP

Agile

The Cloud

Data-First Development

Older work

Coconut Palm Software home


Donate Bitcoin:

1Ecnr9vtkC8b9FvmQjQaJ9ZsHB127UzVD6

Keywords:

Kubernetes, Docker, Streaming Data, Spark, Scala, Clojure, OSGi, Karaf, GCP, AWS, SQL

Disclaimer:

Everything I say here is my own opinion and not necessarily that of my employer.

blog:simplifying_swt_with_scala

Simplifying SWT with Scala

Previously, I promised to show how to radically simplify SWT user interface development using Scala. In this article, we will examine how to do just that.

Our approach will be as follows:

  1. I will present a Java class designed to plug into the Scala RCP application we created previously.
  2. We will translate that Java class into Scala using as close to a 1:1 representation as is reasonable.
  3. We will repeatedly ask, “Can we make the Scala code nicer?” and incrementally improve our example.

Our first steps will use features of the Scala language itself to make our code clearer. The last step will use a new domain-specific language (DSL) written in Scala called XScalaWT, which is a successor to the XML-based XSWT project.

I think you will be very pleased by the result. ;-)

The example application

The application we will develop is a simple converter between Fahrenheit and Celsius temperatures. Entering a Fahrenheit temperature and clicking its associated button will convert the value to Celsius and place the converted value into the Celsius field:

Of course, you can do the reverse also:

The Java code to accomplish this is quite straightforward:

public class TemperatureJava extends Composite {
	public TemperatureJava(Composite parent, int style) {
		super(parent, style);
		setLayout(new GridLayout(2, true));
 
		new Label(this, SWT.NULL).setText("Fahrenheit");
		new Label(this, SWT.NULL).setText("Celcius");
 
		final Text fahrenheit = new Text(this, SWT.BORDER);
		applyLayoutData(fahrenheit);
 
		final Text celcius = new Text(this, SWT.BORDER);
		applyLayoutData(celcius);
 
		Button fahrenheitToCelcius = new Button(this, SWT.PUSH);
		fahrenheitToCelcius.setText("Fareinheight -> Celcius");
		fahrenheitToCelcius.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				double fDouble = parseDouble(fahrenheit.getText());
				double cDouble = (5.0 / 9.0) * (fDouble - 32);
				celcius.setText(Double.toString(cDouble));
			}
		});
		applyLayoutData(fahrenheitToCelcius);
 
		Button celciusToFahrenheit = new Button(this, SWT.PUSH);
		celciusToFahrenheit.setText("Celcius -> Fareinheight");
		celciusToFahrenheit.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				double cDouble = parseDouble(celcius.getText());
				double fDouble = (9.0 / 5.0) * cDouble + 32;
				fahrenheit.setText(Double.toString(fDouble));
			}
		});
		applyLayoutData(celciusToFahrenheit);
	}
 
	private void applyLayoutData(Control c) {
		GridDataFactory.defaultsFor(c).applyTo(c);
	}
 
	private double parseDouble(String s) {
		return Double.parseDouble(s);
	}
}

As we highlighted in the previous article, Scala's default constructor is simply the body of the class. Noting this fairly minor structural difference and translating the code line-by-line into Scala yields the following equivalent Scala class:

package com.coconut_palm_software.scalarcp.view
 
import org.eclipse.swt.SWT
import org.eclipse.swt.widgets._
import org.eclipse.swt.layout._
import org.eclipse.swt.events._
import org.eclipse.jface.layout._
 
class Temperature(parent: Composite, style : Int) extends Composite(parent, style) {
  def applyLayoutData(c : Control) = GridDataFactory.defaultsFor(c).applyTo(c)
  def parseDouble(s : String) = java.lang.Double.parseDouble(s)
 
  setLayout(new GridLayout(2, true))
 
  new Label(this, SWT.NULL).setText("Fahrenheit")
  new Label(this, SWT.NULL).setText("Celcius")
 
  val fahrenheit = new Text(this, SWT.BORDER)
  applyLayoutData(fahrenheit)
 
  val celcius = new Text(this, SWT.BORDER)
  applyLayoutData(celcius)
 
  val fahrenheitToCelcius = new Button(this, SWT.PUSH)
  fahrenheitToCelcius.setText("Fareinheight -> Celcius")
  fahrenheitToCelcius.addSelectionListener(new SelectionAdapter() {
    override def widgetSelected(e : SelectionEvent) = {
      val fDouble = parseDouble(fahrenheit.getText())
      val cDouble = (5.0/9.0) * (fDouble-32)
      celcius.setText(cDouble.toString)
    }
  })
  applyLayoutData(fahrenheitToCelcius)
 
  val celciusToFahrenheit = new Button(this, SWT.PUSH)
  celciusToFahrenheit.setText("Celcius -> Fareinheight")
  celciusToFahrenheit.addSelectionListener(new SelectionAdapter() {
    override def widgetSelected(e : SelectionEvent) = {
      val cDouble = parseDouble(celcius.getText())
      val fDouble = (9.0/5.0) * cDouble + 32
      fahrenheit.setText(fDouble.toString)
    }
  })
  applyLayoutData(celciusToFahrenheit)
}

Can we do better than this?

Factor out data type conversions into their own methods

First, let's try to make our event handlers clearer.

When we look at the event handlers, the first thing that we notice is that 2/3 of the code is devoted to converting between data types and 1/3 of the code implements the actual algorithm.

Among dynamic language advocates, the need for explicit type conversions like we see here indicates that strong typing is a bad idea. However, Scala has a strongly-typed answer to this problem: factor out the type conversions into separate data type conversion methods. These methods are applied implicitly to anything in their lexical scope. The result is that using Scala implicit type conversions, you can have your strong safety, and enjoy clear code too.

How can we do this in our particular case?

There are two type conversions going on here:

  1. The contents of the Text widget is converted to a Double.
  2. The Double which is the result of the calculation is converted back to a String for display.

If we squint at the first type conversion enough, we notice that what is actually going on here is similar to Java 5 autoboxing/unboxing. We have a numeric value that is contained inside an object. We want that numeric value to be auto-unboxed for us so that we can compute based on it.

Conversion back to the String is more or less the same thing happening in reverse. In this case, we would just like our Double to behave the same way it would if it were being used inside a string concatenation expression: ie: we want the toString method to be called automatically when the expected data type (ie: the argument to setText) is a String.

Here is our example, rewritten again using Scala implicits to extend auto-unboxing to SWT Text widgets and auto-convert Double back to a java.lang.String: (For brevity, I have not repeated the package statement or the imports.)

class Temperature2(parent: Composite, style : Int) extends Composite(parent, style){
  def applyLayoutData(c : Control) = GridDataFactory.defaultsFor(c).applyTo(c)
  implicit def unboxText2Double(t : Text) = java.lang.Double.parseDouble(t.getText())
  implicit def convertDouble2String(d : Double) = d.toString
 
  setLayout(new GridLayout(2, true))
 
  new Label(this, SWT.NULL).setText("Fahrenheit")
  new Label(this, SWT.NULL).setText("Celcius")
 
  val fahrenheit = new Text(this, SWT.BORDER)
  applyLayoutData(fahrenheit)
 
  val celcius = new Text(this, SWT.BORDER)
  applyLayoutData(celcius)
 
  val fahrenheitToCelcius = new Button(this, SWT.PUSH)
  fahrenheitToCelcius.setText("Fareinheight -> Celcius")
  fahrenheitToCelcius.addSelectionListener(new SelectionAdapter() {
    override def widgetSelected(e : SelectionEvent) = celcius.setText((5.0/9.0) * (fahrenheit - 32))
  })
  applyLayoutData(fahrenheitToCelcius)
 
  val celciusToFahrenheit = new Button(this, SWT.PUSH)
  celciusToFahrenheit.setText("Celcius -> Fareinheight")
  celciusToFahrenheit.addSelectionListener(new SelectionAdapter() {
    override def widgetSelected(e : SelectionEvent) = fahrenheit.setText((9.0/5.0) * celcius + 32)
  })
  applyLayoutData(celciusToFahrenheit)
}

That's better. Now the data type conversions inside our selection listeners no longer obscure the actual work that we are performing there.

And the implicit data type conversions are restricted to the scope of this class, so they won't leak out and accidentally affect something they shouldn't.

Notice also that switching to implicit data type conversions gives us a second benefit: When a method is one line long, you can lose the curly braces.

But can we do even better?

Improving the event handling even more

We started by simplifying and abstracting our event handlers so that their code contains nothing but the algorithm that they actually implement.

There is one aspect of this that we have yet to simplify: our event handlers are anonymous inner classes.

But Scala is a functional language.

Put another way, our SelectionAdapters really implement a function accepting a SelectionEvent and returning void (in Java terms), or Unit (in Scala terms). In functional programming terminology, this is written:

{SelectionEvent ⇒ Unit}

and read “a function mapping a SelectionEvent to Unit” where “Unit” here roughly corresponds with “void” in Java.

In Scala, this is a real type. As-in, I can write a function with this type signature that does the same thing as the SelectionListener event handler as follows:

{e : SelectionEvent => celcius.setText((5.0/9.0) * (fahrenheit - 32)) }

(Although this function accepts a SelectionEvent, it ignores that event in the implementation.)

If we could just pass one of those anonymous selection event functions directly to addSelectionListener, we could lose all of the anonymous inner class boilerplate and write code like this:

fahrenheitToCelcius.addSelectionListener({e : SelectionEvent => celcius.setText((5.0/9.0) * (fahrenheit - 32)) })

This is definitely clearer than what we had before.

There is one problem with this, however. As we described earlier, the data type of the anonymous function above is:

{SelectionEvent ⇒ Unit}

but the data type of #addSelectionListener is:

SelectionListener

In other words, the data type we would like to use doesn't match the expected data type.

But we just saw that Scala has a way to define implicit data type conversions so that we can specify the data type conversion once and Scala will automatically apply it for us whenever we need it. Can we use Scala implicits to automatically convert a function of type

{SelectionEvent ⇒ Unit}

to a:

SelectionListener

?

If it walks like a duck...

It turns out that we can, but in order to do it, we first have to understand a bit about how Scala implements first-class functions, and then apply Scala's static duck-typing to the problem. But don't worry, it's really easy. :-)

In Java, if you want to define a function that you want to pass around as data, you normally define a Runnable:

Runnable runnable = new Runnable() {
   public void run() {
      // do something here
   }
}
executeTheRunnableSomewhereElse(runnable)

It turns out that Scala actually implements its first-class functions the same way–as function objects. As gerunds, if you will; nouns that have been verbed. It's just that Scala's function objects use a different method name than Runnable does. Instead of “run”, Scala uses “apply”.

Consequently, the SelectionEvent function shown earlier in Scala is translated to something roughly like the following:

val selectionEventFunction = new Object() {
  def apply(e : SelectionEvent) = {
    celcius.setText((5.0/9.0) * (fahrenheit - 32)) 
  }
}

Knowing this, we can now write a Scala generic type that describes the type of all functions accepting a SelectionEvent and returning Unit (ie: void if you're coming from Java):

[T <: { def apply(e : SelectionEvent) : Unit }]

This can be shortened to:

[T <: { def apply(e : SelectionEvent) }]

In both of these examples, T is some subclass of any class that implements an apply method accepting a SelectionEvent and returning Unit.

We don't care what T's class name actually is or what interfaces/traits it implements; only that it has an apply method with the appropriate signature.

Now that we have a way to describe a generic T, we can write a function to convert any T of this sort into a SelectionListener.

  implicit def func2SelectionListener[T <: { def apply(e : SelectionEvent) }](func : T) = new SelectionAdapter() {
    override def widgetSelected(e : SelectionEvent) = func.apply(e)  // just call the function using it's apply method
  }

and the rest of the code pretty much falls out from there:

class Temperature3(parent: Composite, style : Int) extends Composite(parent, style) {
  def applyLayoutData(c : Control) = GridDataFactory.defaultsFor(c).applyTo(c)
  implicit def unboxText2Double(t : Text) = java.lang.Double.parseDouble(t.getText())
  implicit def convertDouble2String(d : Double) = d.toString
 
  implicit def func2SelectionListener[T <: { def apply(e : SelectionEvent) }](func : T) = new SelectionAdapter() {
    override def widgetSelected(e : SelectionEvent) = func.apply(e)
  }
 
  setLayout(new GridLayout(2, true))
 
  new Label(this, SWT.NULL).setText("Fahrenheit")
  new Label(this, SWT.NULL).setText("Celcius")
 
  val fahrenheit = new Text(this, SWT.BORDER)
  applyLayoutData(fahrenheit)
 
  val celcius = new Text(this, SWT.BORDER)
  applyLayoutData(celcius)
 
  val fahrenheitToCelcius = new Button(this, SWT.PUSH)
  fahrenheitToCelcius.setText("Fareinheight -> Celcius")
  fahrenheitToCelcius.addSelectionListener({e : SelectionEvent => celcius.setText((5.0/9.0) * (fahrenheit - 32)) })
  applyLayoutData(fahrenheitToCelcius)
 
  val celciusToFahrenheit = new Button(this, SWT.PUSH)
  celciusToFahrenheit.setText("Celcius -> Fareinheight")
  celciusToFahrenheit.addSelectionListener({e : SelectionEvent => fahrenheit.setText((9.0/5.0) * celcius + 32) })
  applyLayoutData(celciusToFahrenheit)
}

This is definitely better than we started with. We've eliminated most of the static type checking-related boilerplate code with a few strategically-written implicit conversions.

The only repetitive code that is left is all of those repeated variables, each time we make a method call.

Is there a way we can do even better?

Introducing XScalaWT

Yes, there is.

What I am about to introduce is an experimental library I've been working on for really radically simplifying SWT and Eclipse development using Scala. Its ideas are similar to XSWT, but the language syntax is Scala, not XML. This provides the following benefits:

  • Strong type checking. The compiler can nearly always tell you if your layout will load correctly. Sorry, you still have to make it look good yourself. ;-)
  • Automatic tool support. Since Scala is the language, you get content assist and compile-time syntax checking for free from the Scala Development Tools.
  • Integration with, and “scripting” in Scala for free (because XScalaWT is Scala).

Hierarchical code for user interface hierarchies

XML is a hierarchical language. So, in XML, it's natural to write something like:

<composite>
   <layout x:class="fillLayout"/>
   <label text="Hello, world"/>
</composite>

Notice how the hierarchical nature of XML eliminates all of the extra housekeeping variables that you use to keep Java happy? The result is that you can focus more easily and automatically on the hierarchical structure of your graphical layout rather than on the nitty-gritty of keeping the Java compiler happy.

XScalaWT takes this idea one step farther. By making the user interface language the same as the application language, the result is both a more natural syntax than even XML, and automatic tight integration with Scala code, since XScalaWT code is Scala code.

Enough introduction. Here is our example, rewritten using XScalaWT:

class Temperature4(parent: Composite, style : Int) extends Composite(parent, style) {
  implicit def unboxText2Double(t : Text) = java.lang.Double.parseDouble(t.getText())
  implicit def convertDouble2String(d : Double) = d.toString
 
  var fahrenheit : Text = null
  var celcius : Text = null
 
  this.contains (
    _.setLayout(new GridLayout(2, true)),
 
    label("Fahrenheit"),
    label("Celcius"),
 
    text(fahrenheit = _),
    text(celcius = _),
 
    button("Fareinheight => Celcius", {e : SelectionEvent => celcius.setText((5.0/9.0) * (fahrenheit - 32)) }),
    button("Celcius -> Fareinheight", {e : SelectionEvent => fahrenheit.setText((9.0/5.0) * celcius + 32) })
  )
}

Now we've finally gotten what we were looking for.

The function of the code is now fully apparent based on its structure. Scanning visually through the code is sufficient for gaining a high-level understanding of what it does, and then you can just read the parts that you actually care about.

Notice that we no longer need the implicit that allows us to treat functions as event handlers. XScalaWT does that for us automatically.

The astute reader will wonder what happened to the GridData assignments. The answer is that if you don't supply GridData of your own, and the parent has a GridLayout XScalaWT automatically uses the GridDataFactory to supply default GridData. But you can easily supply your own GridData too.

Those who don't already know Scala are certainly wondering how Composite earned itself a “contains()” method. The short answer is that Scala implicits provide a type-safe way of effectively adding methods to classes that you don' “own”. XScalaWT uses this to add the #contains method to all subclasses of Widget in the SWT hierarchy. And since implicits are lexically scoped, this method is only added if you have listed XScalaWT in your class's imports.

A bit of how this works

XScalaWT is basically one long nested vararg-style argument list of function objects along with a thin functional wrapper around SWT. There is also a fully-generic syntax supplying access to 100% of SWT's features, but this syntax depends on scala.reflect.Manifest, a library feature that is basically “internal provisional” in the current Scala release. I expect that future Scala releases will supply a fully supported way to implement the fully-generic XScalaWT syntax.

If you want to deeply understand how this works, I recommend reading the blog post I link to in the acknowledgements section along with the source code. Also, there are complete code examples in the XScalaWT distribution. If you don't know Scala yet, the book Programming in Scala by Odersky et al is excellent.

Conclusion

We have seen how using straight Scala code, we can simply SWT programming considerably.

But Scala gives us the power to define internal domain specific languages (DSLs). By using this power to define an internal DSL for SWT, we can supply the power and expressiveness previously known only to loosely-typed languages like JRuby, but with all of the benefits of a strongly-typed language that is tightly integrated with Java at the bytecode level.

If you want to try this

If you want to try the code listed in this blog, you will need the code from the previous blog, along with XScalaWT. The code is also available in Mercurial repositories over at bitbucket.org as:

After you get the code, there are two known issues I have with the SDT you should be aware of.

  1. It often doesn't properly update classpaths when OSGI manifests are updated. The fix is usually to choose “Project | Clean…” from the menu and rebuild the whole workspace.
  2. You have to list all bundle dependencies in Scala-based bundles. For example, if you import org.eclipse.ui, you have to explicitly import org.eclipse.swt also.

Other than this, there are a few development glitches like not always having content-assist when you should, but things seem to overall work pretty smoothly.

Project status

I have currently implemented about 85% coverage over SWT, including all of the easy parts and some of the hard parts.

Eventually, I aim to cover all of SWT and to be compatible with RAP, so you will be able to use XScalaWT to build AJAX-style Web 2.0 applications if you wish.

If you want the code, it's currently available as a Mercurial repository at http://bitbucket.org/djo/xscalawt/ and you should consider the implementation to be experimental for now.

Acknowledgements

XScalaWT's ideas derive mainly from XSWT and Glimmer, in about that order.

The implementation has more people to acknowledge than I could mention here, but I particularly want to thank the author of the blog Making SWT Shine with Scala, the Scala mailing list, and the comp.lang.scala.tools newsgroup for all providing valuable input.


Edit: Fixed typo in XML example

~~LINKBACK~~ ~~DISCUSSION:closed~~

blog/simplifying_swt_with_scala.txt · Last modified: 2014/10/17 22:08 (external edit)