Sunday, May 22, 2011

Haskell + FFI + Java + SWT: crazy, maybe, but working!

A few years ago I noted that being able to mix Haskell code and a Java GUI might be a good idea, since Java UIs have matured enormously, and Haskell UI library are still struggling to gain wide adoption, a recent thread on cafe being proof. SWT bindings were mentioned, so I had a look at both the Java and the C SWT code, for windows. Two immediate things sprang to view:
1. The SWT C library is using JNI, duh. It relies on JNI for some operations so you can't just use the SWT.dll in your haskell code that easily
2. The SWT C bindings are very low level, and there is a wealth of Java code on top of that to provide a lot ow glue and boilerplate code for all kind of widgets, and it would be long and painful to rewrite it all in Haskell.

My Java to Haskell code converter only being a dream at the moment (one day...), the next reasonable (ahem) option was then obvious: start a few blown JVM from Haskell and use it to drive SWT classes.
I'm a Java expert, I can say, and my Haskell is slowly coming on, but the glue in between has to be C, so it wasn't a smooth ride, but I finally got it working! So what's happening is this:
- The Haskell applications starts as they all do, in a main :: IO() function
- Haskell invokes some C code that starts a new JVM, passing it a classpath. The C code acts then as a facade hiding some of the intricacies of accessing the JVM
- Haskell uses the foreign C functions to create ands manipulate the SWT Java objects
- I even got callbacks working, meaning you can register a Haskell listener on a SWT button, and hence have Haskell code calculate the results of that button click.

And I have proof! Here's a simple SWT Shell with one button:
When you click the button:

And the handler for the button click is written in Haskell (I have a MVar called count for the number of times the user clicked):
do
    modifyMVar count (\c->do
        let nc=c+1
        let s=if nc==1 then "once." else ((show nc)++ " times.")
        text3<-toJString ("Clicked "++s)
        voidMethod button "setText" "(Ljava/lang/String;)V" [JObj text3]
        return(nc,())
        )


You can see here the invocation of the setText method of the SWT Button, which for the moment requires knowing the exact JNI signature, and the translation from Haskell strings to Java strings.

Of course, there is still loads to do. I probably need to automatically generate bindings to the Java objects to simplify greatly their manipulation, wrap stateful operations in a monad, etc. But this may open a new way for Haskell GUI. Or it may just be a crazy idea that has already taught me a lot about FFI and JNI...