The last post introduced Swing, identified it as an event-driven GUI framework, and talked about the Observer design pattern. In my opinion, Swing could stand to learn a lot from event handling in F# and the .Net Reactive eXtensions framework. Here’s why:
Events are not first-class citizens in Swing, and we cannot treat them as data. Instead, we have to intercept event packets via scoped interfaces (listeners) that are attached to event-producing Swing components. This usually results in creating ad-hoc implementations of listener interfaces. We do not have the ability to subscribe to a single event, or to compose the observation of events into new, compound events. Everything has to be handled inside an encapsulated method of an implemented listener interface.
The typical workflow for attaching a listener to something that
produces events is: Implement a Listener interface/s relative to the
events of interest. Typical Java listener interfaces are broken up
into groupings of handlers for the events they implicitly ask a
component to subscribe to. The consistency amongst groups is that
every method in the Listener interface has the same signature
(SomeEvent -> Void). For example, the
implicitly defines 4 mouse events via its handlers:
mouseClicked :: MouseEvent -> void mouseEntered :: MouseEvent -> void mouseExited :: MouseEvent -> void mousePressed :: MouseEvent -> void mouseReleased :: MouseEvent -> void
To handle mouse events for a JPanel, you tell the JPanel to attach a
mouselistener with appropriately overridden methods. The idiomatic
Clojure way to do this is to evaluate the
method of the swing component, and pass it a Proxy of a
(.addMouseListener mypanel (proxy [MouseListener]  (mouseClicked [e] (println e))))
The net effect is that performing non-trivial GUI programming in Java (and to a lesser extent Clojure) requires dealing with attaching listeners, and never getting to see the data. Proxy mitigates a lot of this, but there is, I think, a better way…a way that’s more functional….
.Net handles this by publishing specific events of interest, and
allowing anything to subscribe to them. Prior to the .Net Reactive
eXtensions, and (in my case) the treatment of events in F#, .Net folks
had to use delegates to handle events. Delegates are just
statically-typed wrappers around functions….really a hacked lambda
function. In fact, the F# team realized this (along with the Rx
guys), and decided to
promote events (like functions) to first-class status.
The end result was an elegant library for extending the basic sequence
scan, etc.) onto .Net events,
to produce elegant and composable events. You get the ability to
treat events as streams of observed values, driven by IO, which can be
composed just like functions.
During my stemming, I realized that it’d be really useful to implement the .Net/F# view of first-class-events in Clojure. With a suitable infrastructure in place, I could then write some plumbing libraries to extract the events described in the existing Java Listener interfaces, turning them into streams of observable values amenable to sequence operations, rather than hiding them away behind Swing’s interfaces. Separating the data from operations that work on data felt like a more functional, and Clojurian approach.
Next: an implementation of observers in Clojure, generic events, event combinators, and using reflection and macros to “wrap the crap.”