The point is missed

Museum of public mistakes and unfinished projects

Making SWT shine with Scala

with 34 comments

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:

SWT scala screenshot

 

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 <: {def setText(txt:String)}](txt:String)(subject:T) = {
    subject.setText(txt)
}

Pretty straight forward right? Look again at the generic type 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?

About these ads

Written by johlrogge

January 6, 2009 at 8:48 pm

Posted in Ruby, scala, SWT

Tagged with ,

34 Responses

Subscribe to comments with RSS.

  1. Hi, nice work. I would certainly prefer to use something like this over ruby or java.

    Channing

    January 6, 2009 at 10:29 pm

  2. Me, too. How do you access the elements after constructing them (for example to enable/disable them)…they don’t have any name or unique-id?

    Daniel

    January 6, 2009 at 10:53 pm

  3. Thanks for the input.

    Daniel: This framework is very much in it’s infancy. What is possible to do even now is to provide a callback function in the setup-list for the control you want to manipulate. It would look something like this:

    edit (mediator.setEdit)

    where setEdit on the mediator would have the following signature:

    def setEdit(control:Text)

    That way, the Text-control would be assigned to the mediator as soon as it is created. Basically, you can pass whichever function that takes a Text as a parameter and it will be called when the edit is created.

    I will soon have a reason to experiment with different ways of interacting with controls. I hope I will come up with something more elegant in time. Of course the obvious way is to look how it has been solved in Glimmer :)

    Thanks for your comments, keep them coming. I appreciate them!

    johlrogge

    January 6, 2009 at 11:27 pm

  4. Somebody else did a Swing scene graph API in Scala. His approach was a bit different (attribute/value pairs using symbols), but might be worth looking at for other bits like binding events.

    http://compulsiontocode.blogspot.com/2007/10/gui-building-with-scala.html

    Frankly, all of this stuff will be much easier and cleaner if/when Scala gets named and optional parameters.

    James Iry

    January 7, 2009 at 12:23 am

  5. It should definitely became a library. Sun decided to use JavaFX/F3 to improve Swing. Why not use Scala to improve SWT/JFace?
    Sunny side: JavaFX – Swing – Netbeans – Jigsaw
    Dark side: Scala – SWT – Eclipse – OSGI

    I prefere the dark side :)

    Mike

    January 7, 2009 at 3:06 am

  6. Cool, this looks to me like Groovy’s builder syntax.

    K

    January 7, 2009 at 7:26 am

  7. Wow. I had no idea Scala could do that declarative duck typing thing. That’s pretty awesome.

    nathan

    January 7, 2009 at 5:53 pm

  8. Thank you for providing intersting examples for how advanced Scala’s features can be applied. Considering proplem of accessing controls: it can be addressed in simple and elegant way:

    in lib:

    def keep[T](setter:T=>Unit)(widget:T) = setter(widget)

    and in application:

    class SomeController {
    var Button myButton

    def createView() = …
    button(
    keep(myButton = _),
    text(“My Button”))
    }

    Alexey Tarasevich

    January 7, 2009 at 10:32 pm

  9. Oh, pardon. No keep function is needed. Just:

    button(myButton = _,
    text(”My Button”))

    Alexey Tarasevich

    January 7, 2009 at 10:54 pm

  10. Thanks for all the feedback, I’m overwhelmed with the attention this little post is getting. I did not expect that (over 500 hits since yesterday, thanks whoever posted on DZone and Reddit and all of you who must have shared this via google reader and other means (all according to my statistics)).

    I wanted some more interactive example and setup to mimic the example at: http://compulsiontocode.blogspot.com/2007/10/gui-building-with-scala.html

    I read Alexeys keep-function just when I was done. I put it in there as well as an example. It’s a keeper, very nice, I’m impressed (I did not get the second suggestion with only myButton = _ to work, the function must return a function that takes a label (so that calls for the keep function).

    Here is the code, it demonstrates listening to an event and updating a label. It’s a bit patchy and messy at the moment and Alexeys keep function is there because I like it but not really used:

    var theText = (x:String) => {println("clicked")}
    var theLabel:Option[Label] = None

    def keep[T](setter:Option[T]=>Unit)(widget:T) = setter(Some(widget))

    val theShell = shell(
    text("caption"),
    rowLayout,
    button(
    text("Press me!"),
    onSelect((e:SelectionEvent) => {theText("button pressed")})
    ),
    label(
    text("Button not pressed"),
    keep(theLabel = _),
    updateWith(theText = _)
    )
    )

    This displays a GUI with a button and a label. The label initially reads “Button not pressed” and when the button is clicked the label reads “button pressed”.

    It’s rough around the edges but works. Any input on improvements in any direction are most welcome. I believe that I should be able to put more Scala power to use to get an even more literal interface with some operator magic but I’m afraid my Scala skills doesn’t quite cut it yet :s It’s a lot of trial and error at the moment :D

    johlrogge

    January 8, 2009 at 12:20 am

  11. A clear mind after a good nights sleep was all it took to make Alexey’s second suggestion to work. Make keep implicit (Since I used an Option I hade to make a second implicit conversion from any value to a Some option of the same.

    These conversions can of course be tucked away in the framework so that an enduser would not have to write them:


    var theLabel:Option[Label] = None

    implicit def toSome[T](value:T) = Some(value)
    implicit def keep[T](setter:Option[T]=>Unit)(widget:T) = setter(Some(widget))

    shell(
    text("caption"),
    label(
    text("Button not pressed"),
    theLabel = _
    )
    )

    I’m a bit worried that the implicit conversion from anything to an option could get me into trouble later. But in this context it works.

    johlrogge

    January 8, 2009 at 8:06 am

  12. Great article! However, WordPress seems to have messed with some of your source code overnight, e.g.:

    “def group(setups <img src=”http://s.wordpress.com”

    Yesterday there was a smiley because of “:(“, now it’s the HTML code for a smiley.

    Daniel Lichtenberger

    January 11, 2009 at 11:07 am

  13. Daniel, Thanks for the feedback and for bringing the smileys issue to my attention. It should be fixed now.

    johlrogge

    January 11, 2009 at 11:24 am

  14. Hey this looks great!
    Looking forward to check it out, if you go open source!

    ImInterestedInThis

    January 12, 2009 at 2:10 pm

  15. [...] a previous post I wrote about how to make a DSL for creating SWT-guis in Scala. My source of inspiration was [...]

  16. I like it. I wonder if the syntax couldn’t be made a bit cleaner by using case classes and pattern matching, though.

    At any rate, I would like to see it open-source. At the very least, it would serve as basis and competition for other efforts, and anything that makes GUIs simpler is a good thing in my book! :-)

    Daniel

    January 27, 2009 at 10:10 pm

  17. As K mentioned it looks a lot like the groovy SWT module. Have a look at http://docs.codehaus.org/display/GROOVY/groovy-swt+-+Comparing+the+java+and+the+groovy+code.

    Frank

    January 28, 2009 at 11:37 am

  18. [...] decided to make the source for my DSL available under the beerware license. I choose beerware for several [...]

  19. Daniel:

    If you find the time I would love to see how this could be improves with pattern-matching (or in any other way for that matter). I released the code (nothing fancy just a zip) See the pingback for “Buy me a beer” above.
    I’d love if what I started turns into something that makes GUI’s easier to write. It’s a good thing in my book aswell :)

    Frank:

    Yes, seeing libraries like Glimmer, the Groovy JFace-builder makes java feel more and more like running a marathon with concrete shoes. It’s a shame that closures seems to be out of java 1.7, I think it would be a big improvement to the current state of things.
    Obviously there are a lot of sources of inspiration out there and Scala seems to have what it takes to match them (except for dynamic meta-programming but…)

    johlrogge

    January 28, 2009 at 10:41 pm

  20. Joakim, speaking of structural types, they are implemented through reflection, and they are VERY slow. Now, slowness is relative when you are driving a GUI, but it’s something to keep in mind.

    I’ll check out Buy me a Beer, and see what could or could not be done. I might very well be wrong, but I have that itchy feeling you get when you KNOW you are missing something…

    Daniel

    January 30, 2009 at 12:49 am

  21. Daniel,

    Thanks! I did not know that but it makes a lot of sense now that I know that. In fact, I don’t see any other reasonable way to implement it if you don’t want to restrict yourself to what is available at compile-time (compared to “when your code is used as a library in someone else’s code).
    Do you know any benchmarks on structural types? My experience is that the slowness of reflection when done right (looking up the methods once, invoking many) has an undeserved reputation for being slow (sure there is some overhead but that overhead tends to be overrated for most practical applications).
    With some exception (that I can’t recall) the structural types are used when building the GUI so when the GUI is already built there should be no penalty (or is the object wrapped in a proxy?).

    (for those of you that did not figure it out, I’m not sure if I got what structural types was because I read about them in a blog yesterday or if it would be obvious if I hadn’t. Structural types is the correct name for what I called “declarative duck-typing” in my post)

    Go with your gut-feeling! I trust tummy-driven development much more than plan-driven development (tends to be more efficient). Whether your intuition leads you right or to a dead end I’m very interested in the results and the learning experience.

    At the moment I’m trying to make my example work with QT, I’ll let you all know how that works. So far QT is very different when it comes to how you use layout-managers and event handling (that seems kind of clever with some reflection fairy-dust on top)

    johlrogge

    January 30, 2009 at 7:15 am

  22. Cool stuff, this looks very elegant. The open question seems to be: how can you fill the widgets with data, and respond to events?

    Boris Bokowski

    February 10, 2009 at 3:23 am

  23. Hi Boris, thank you for your comment. I have been totally absorbed in QT and tons of other things lately so I have paused in the middle of addressing this. (I’m also currently looking into the parser combinator libararies in Scala for isnpiration).

    I have something that is not all bad but still experimental. I attach an example of a test-gui here in the comment since I will probably not get around to make a post in a while (lot’s of conference submissions and internal company work gets in my way)


    package com.agical.rosetta.gui

    import com.agical.gsl.Swt._
    import com.agical.gsl.present
    import com.agical.gsl.observe

    object Gui {
    def horizontalFill(cols:Int) = horizontal(
    fill,
    span(cols),
    grabExcessSpace
    )_

    val model = new RosettaModel()

    val theShell = shell(
    text("Rosetta"),
    gridLayout(
    columns(2)
    ),
    progressBar(
    horizontalFill(2),
    observe(maximum) on (() => model.max),
    observe(selection) on (() => model.current)
    ),
    tree(
    horizontalFill(2),
    vertical(
    fill,
    grabExcessSpace
    ),
    present(items) using model.specItems
    ),
    button(
    text("run"),
    observe(enabled) on (() => {model.max > 0}),
    onSelect((gsl, e) => {model.run; gsl.update})

    ),
    button(
    text("list"),
    onSelect((gsl, e) => {model.find; gsl.update})
    )
    )

    def main(args:Array[String]) {
    theShell()
    }
    }

    The above is a GUI for a testrunner. It has a progressbar and a tree showing the tests. When running the progressbar… progress… and the list-button fills the tree if you just want to see which tests there are… This is semi dirty. At the moment a lot of tree-details is pushed to the model. I’m sure this can be fixed though but requires some tinkering…

    So, I’m working on it. Check out the other posts too, the source of the library is downloadable from the “buy me a beer”-post.

    johlrogge

    February 10, 2009 at 8:59 am

  24. nice stuff! i wonder if scala could be used to develope a sheet DSL as is described in http://stlab.adobe.com/group__asl__overview.html ?

    dré

    November 26, 2009 at 9:38 am

    • Sorry for not replying in a while.

      Interesting idea. I think some modifications would have to be done to make it work (due to syntax limitations) but probably you could get pretty close. I (or someone else) would have to look into that. Could work as a source of inspiration.

      /J

      johlrogge

      December 3, 2009 at 10:18 pm

  25. have you ever think to setup a project in sourceforge , google code or eclipse

    jilen

    August 30, 2010 at 12:19 pm

  26. There’s also my own work (that borrows heavily from this, but also from Glimmer and older work I did on an XML-based DSL):

    https://github.com/pieceoftheloaf/XScalaWT

    David Orme

    February 11, 2011 at 2:38 am

  27. I forgot to add for anyone who might be interested that XScalaWT also includes style sheets and Eclipse Data Binding support. :)

    Dave Orme

    David Orme

    February 11, 2011 at 2:43 am

  28. […] kilku mało interesujących przykładach natrafiłem na coś wartościowego. Pomysł jest bardzo interesujący i pokazał mi w którą stronę można się udać. Na podstawie […]

    • Hi :)
      I thought that you could be interested in my battles with SWT. Unfortunately for you the post is in Polish language so I believe you could benefit only by looking at code examples. (but ask me if you wish :) )

      Your code helped me to get started but in the end I’ve come up with something completely different.
      Anyway – thanks for the post, it was very helpful :)

      moriturius

      June 30, 2013 at 3:13 pm

      • moriturius

        June 30, 2013 at 3:14 pm

      • Thanks, I got curious at the time of the pingback. Two questions came to my mind:
        1) what language is this
        2) What does it say
        Google translate provided satisfactory answers two both questions :)

        I’m glad my post is still helpful and that you found inspiration to take it to new levels. I didn’t spend that much time reading your code but the end result looks very neat and your post was an enjoyable read even in google translated form. I can imagine it is even better in polish but brain resisted that challenge :)

        Thanks for writing about your efforts, for linking and for letting me know!

        johlrogge

        July 1, 2013 at 9:13 pm

      • Wow, I’ve just tried to read the post translated via google and… it’s awful :)
        I think I should start writing in english…

        moriturius

        July 2, 2013 at 4:12 pm


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: