At Agile 2008 I was calmly coding together with Daniel Brolund in the open space area. We were interrupted by a guy that asked us if we did any coding in Ruby and if we did he will hold a lightning-talk about a framework he had put together to create SWT-gui:s using JRuby. Interestingly enough I had thought about how GUI-coding could probably be improved in a language such as Ruby so I reluctantly stopped what I was doing and watched the lightning talk. This was the first time I ever saw Glimmer and it totally blew me away!

It must be possible to do in Scala!

I had already started playing with Scala and I thought it would be a cool experiment to see if I could do something similar in Scala. After all, scala is a pretty expressive language.

I made the first attempt a few months ago and I think that qualifies as a failure. I got something to work but all the mutable state and redundant classes I had to create bothered me big time.

I showed what I did to colleague and I got some pointers on how to make my code more function-oriented and hence get rid of some redundancy and mutable state. But my lacking knowledge of Scala and FP prevented me from finishing the task back then.

Success!

Yesterday I gave it another shot and it took me only an evening to make this code:

shell( text("User Profile"), composite ( gridLayout(2, false), group ( text("Name"), gridLayout(2, false), gridData(FILL, FILL, true, true), label(text("First")), edit(text("Bullet")), label(text("Last")), edit(text("Tooth")) ), group ( gridData(FILL, FILL, true, true), text("Gender"), radioButton (text ("Male"), selected), radioButton (text ("Female")) ), group ( gridData(FILL, FILL, true, true), text("Role"), checkBox (text("Student"), selected), checkBox (text("Employee"), selected) ), group ( text("Experience"), rowLayout, gridData(FILL, FILL, true, true), spinner(selection(5)), label {text ("years")} ), button ( text("save"), gridData(RIGHT, CENTER, true, true) ), button ( text("close"), gridData(LEFT, CENTER, true, true) ) ) )

Display this:

Pretty cool! I will admit that I have gracefully stolen most of the ideas from Glimmer. Even the example above is a slightly modified Glimmer-example. I will probably go over it a couple of times to see what I can improve from a Scala perspective. I particulary dislike the gridData functions. I think there is room for improvement there.

How does it work?

The general pattern is simple. Take a look a the group element in the code above. Group is implemented like this:

def group(setups:(Group => Unit)*)(parent:Composite) = { val group = new Group(parent, SWT.NONE) rowLayout(group); setups.foreach(setup => setup(group)) }

If you, like me when I saw my first Scala examples, have some dificulty with the double parameterlist notation it is a shorthand in Scala for returning a function. So the above is a function that takes a variable number of functions that take a Group as the only parameter. Unit in Scala is equivalent to void in java. The group-function will return a function that takes a Composite, parent, as it’s only parameter.

The cool thing about this is that you can pass the first set of parameters (the variable number of functions that takes a Group and return Unit) and save the returned function for later evaluation. When the composite parent is later passed what will happen is the following:

A new Group widget will be created with parent as parent

as a default the layout of the group will be set to row layout, this may be overridden in the next step.

Each function in setup will be applied to the newly created group.

Remember, the setup-functions take a group as a parameter so they can perform whatever action on the Group. You have full freedom to do whatever can be done with a group in any of those functions.

The variable length argument is one of the missing pieces I needed to solve the puzzle, I failed to find information about variable arguments via google. Ok, I did not spend an awful lot of time trying…

What does one of those setup-methods look like then?

Let’s start with something simple, the label:

def label(setups:(Label => Unit)*)(parent:Composite) = { val label = new Label(parent, SWT.NONE) setups.foreach(setup => setup(label)) }

The pattern repeats itself, lazy creation of the label onto the parent composite (for instance a Group) and the posibility to furter customize the label. Now comes the next secret I had to learn in order to solve the puzzle. Take a look at the text-function:

def text[T T. I don’t know what that construct is called, I call it declarative duck-typing, you know if it walks like a duck and quacks like a duck it’s a duck!

Here I declare that T can be any type that has a function named setText that takes a String as an argument and returns nothing. If I did not declare this then the subject.setText(txt) line would not compile. But I’m getting ahead of myself. Why did I need to find this construct?

Several different widgets in SWT has a setText-method, for instance, shell has a setText, label has a setText etc but some controls do not. There is no common interface between all different widgets that has a setText-method. That makes things a bit tricky, my first attempt was to overload different setText-functions, one for label, one for shell etc… This was not only tedious, it didn’t work since the difference between all of those overloaded methods is in the parameterlist of the returned function. Luckily, this little inconvenience forced me to discover this NEAT feature of the Scala-type system. I’m happy I did.

In conclusion

I was able to make a DSL that is very comparable to glimmer which was my original goal. I was able to make the whole thing initialize lazily. Basically I just build a tree of constructing functions and then I sort of light the fuse by passing a shell to the root of that tree and the GUI is created. I did not have to introduce any mutable state other than what is already mutable in SWT. I have a feeling that this approach is very extendable. Maybe the GUI is a good place to introduce Scala in Java-projects? My feeling is that Scala would slowly eat it’s way in to other parts of the system from there :)

Perhaps this is a good candidate for an open source project? Would you want to use it?