The Source for Java Technology Collaboration
User: Password:



Tom Ball

Tom Ball's Blog

Are Closures Just Delegates?

Posted by tball on August 25, 2006 at 02:29 PM | Comments (20)

The recent blogs on closures have left me with a real sense of déjà vu. A few months after the JDK 1.0 released (over ten years ago), Microsoft proposed that the Java language be extended with type-safe method references, which they called delegates. The AWT team I was a part of liked this proposal and pushed for implementing it, as it fit well with the new GUI event model we were designing to support Java Beans. The Java language team shot the proposal down, however, responding publicly with the About Microsoft's "Delegates" white paper, which described how everything you might want from a delegate can be better handled with inner classes. Microsoft responded with The Truth about Delegates, and the two companies eventually went their separate ways, language-wise, with C# supporting delegates, and adding anonymous delegates (which look a lot like what's described in the Java closures proposal) in C# 2.0.

When the Java language team shot down the delegates proposal, the AWT team grumbled quite a bit since delegate-like equivalents are frequently used for event-driven application development (which is undoubtedly why Microsoft's GUI team drove the proposal from their side). But then an interesting thing happened: due to this constraint (no delegates), our event design improved significantly. As the old saying goes, sometimes unanswered prayers are blessings. If we had delegates back then, the AWT might be (even) slower and (even) harder to use than it is today.

I don't mean to suggest closures are evil (I live close enough to the old Xerox Parc site that some Smalltalk fanatics might pass messages to me wrapped around a rock!), but language features do impact API and performance work. In this case, the AWT has a design constraint that was better addressed without using delegates: lots of different event methods that need to be dispatched to interested components very quickly. This may be hard to believe, but in Mustang the AWT and Swing define over ninety component events, while toolkit performance must keep improving even as the complexity of desktop applications increases.

In 1.0, the AWT routed events up component hierarchy until a component "consumed" the event, which doesn't scale well to even small applications due to the number of events fired during normal usage. What made this worse was that frequently fired events such as mouse movement or scrolling events weren't interesting to most applications, so their dispatch took the longest since no component consumed them. Think I'm exaggerating? During the early Swing days, Hans Muller's favorite performance test was to open a scrolling window with a lot of text or a big graphic, click-and-hold on the vertical scrollbar, and wiggle it up and down quickly to check for lags. Not the most elegant test, but it quickly and reliably uncovered performance issues because thousands of events get fired during that wiggling, and if they aren't dispatched and handled quickly, then the whole GUI bogs down.

But what does this have to do with closures/delegates/better design? I'm getting to that. What's the fastest performance Big O number? O(log N)? O(1)? Try zero. The fastest code is the code that isn't executed. That's why in 1.1 the AWT moved to a publish-and-subscribe event model (the "new" event model), so that events the application didn't care about weren't dispatched. This is great for performance, but it makes the API more complex because you need a separate method for each event, thus making it harder to use. It also encourages code duplication in similar event handlers, making the application potentially less robust (fixing a bug in multiple places is less reliable than just one).

We defined groups of GUI events to address the API complexity issue, by declaring a shared data instance (Event class) and event interface (Listener) for each group. The advantage of listeners is that related event methods are in one class, each of which share a common Event class for event data. This reduced the complexity and data sharing issues to a certain extent. The problem then became that developers didn't want to write event handlers for events they weren't interested in, so Adapter classes were created so uninteresting events get no-op'd. This combination of listener interfaces with Event objects and Adapter classes balances the optimization needs of the toolkit with developer ease-of-use.

Tom, WHAT DOES THIS HAVE TO DO WITH CLOSURES/DELEGATES/BETTER DESIGN?!? Simple: multiple event Listener interfaces and Adapter classes aren't possible with delegates or closures, as each event requires a separate delegate. If delegates had been added to 1.1, the AWT team would have used them instead of the current design (since delegates are well-suited for event dispatch systems), and AWT development would now be (even) harder while performance would be (even) slower.

I'm therefore on the fence regarding adding closures to Java. While I like functional programming and agree that certain problems can be coded more succiently using closures, I'm worried about how overuse of them can hurt performance and API design. Read the whitepapers linked above, and then tell me what points I missed.

Bookmark blog post: del.icio.us del.icio.us Digg Digg DZone DZone Furl Furl Reddit Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment

  • IMO, closures and (named) delegates are superficially similar, but the intent is different. Delegates are about the class; closures are about the immediately surrounding code.

    It's not too difficult to emulate delegates with closures. Just replace obj.something(myMethod); with obj.something() { myMethod(); }. Add parameters as needed.

    As for performance issues - aren't the HotSpot guys paid to make everyone else's life easier? I don't suppose the event firing mechanism actually takes up significant time (although I don't have figures).

    Posted by: tackline on August 25, 2006 at 03:00 PM

  • The point I was trying to make is that if we had delegates back when we were designing this system update, it might have limited what ideas we came up with. All language features have trade-offs, but lately all I've heard about closures is how wonderful they are. I suspect they'll be quite useful in my work if they are added, but I was hoping folks would review the reasons why they weren't added in the past to see if they still apply.

    Posted by: tball on August 25, 2006 at 04:08 PM

  • Excellent article, Tom.

    Not to sound bitter, but aren't closures with the attendant (and woolly-mouthed, IMHO) syntax (and extra non-idiomatic function types, and...) a foregone conclusion? If Bracha and Gosling are in on it, the rest of us don't stand a chance. :-)

    Posted by: ljnelson on August 25, 2006 at 04:47 PM

  • I liked this one: It is unlikely that the Java programming language will ever include this construct.

    Posted by: kirillcool on August 25, 2006 at 06:13 PM

  • Tom: the VM techniques that make the anonymous-class-based idioms efficient apply to closures too. I wouldn't expect to see much difference in performance.

    Posted by: gafter on August 26, 2006 at 09:58 AM

  • I am very confident that the closures proposal will lead to efficient code; with you folks tackling the language issues and the javac and HotSpot teams handling efficient generation, it should be a great new feature. My question is how are these closures so different from delegates, such that today's closures can stand up to the criticism of Sun's white paper. Maybe we're all just older and wiser now. :-)

    Posted by: tball on August 26, 2006 at 10:31 AM

  • Interfaces and anonymous instances already give you expressive power comparable to delegates, so adding delegates would be pointless now. Closures are much more expressive for some kinds of APIs that currently can't be expressed in Java at all. See my latest "use cases" blog post.

    Posted by: gafter on August 26, 2006 at 10:39 AM

  • Here is a pointer to Neal's post, which I highly recommend reading:

    http://gafter.blogspot.com/2006/08/whats-point-of-closures.html

    Posted by: tball on August 26, 2006 at 10:45 AM

  • I wanted to point out that the approach of "grouping" common events into a single interface is still possible with delegates -- you have a single delegate signature that represents that group (the interface). And in the Event object that is passed in as an argument, you simply have a property (an Enum?) that represents which concrete event is being fired.

    So instead of having:

    public class MouseEvent extends Event {
    ...
    }
    public interface MouseListener {
    void mouseEntered(MouseEvent);
    void mouseClicked(MouseEvent);
    void mouseReleased(MouseEvent);
    void MouseExited(MouseEvent);
    ...
    }
    public Abstract class MouseAdapter implements MouseListener {
    // No-op methods
    ...
    }

    You simply have:

    public enum MouseEventType {
    MOUSE_ENTERED,
    MOUSE_CLICKED,
    MOUSE_RELEASED,
    MOUSE_EXITED,
    ...
    }
    public class MouseEvent extends Event {
    public MouseEventType getEventType();
    ...
    }
    // Delegate signature
    public void mouseListener(MouseEvent);

    In fact if you wanted to improve the efficiency, you can even have the delegate take an array of event objects so that you can batch multiple grouped events into a single "listener" call:

    public void mouseListener(MouseEvent[]);

    That would improve the efficiency by letting the handler simply iterate over a loop of events instead of incurring multiple method calls.

    Mind you, I'm neither advocating nor opposing delegates/closures, just wanted to point out that an efficient and simple event system can be built atop them just as well as the interface/adapter approch. Of course that's a matter of opinion, and I'm quite happy with the current approach :-) just as well.

    Posted by: ebekker on August 28, 2006 at 06:33 AM

  • The different reaction to Delegates and Closures comes from their source.
    Delegates were suggested by MS, no wonder most people (in Java land) did not like.
    Closures are being made popular by Ruby and RoR, they are fashionable nowadays. Look at how many ex-Javaheads are advocating Ruby.

    b.t.w. I do like closures, and wish we can have continuations also.

    Posted by: ahabra on August 28, 2006 at 07:59 AM


  • Hmm. Don't mean to be negative, but I really dislike the grouped event stuff in the Java 1.1 AWT event model. Independent listeners on each individual event would have been much cleaner and easier than the Listener/Adapter hack. That is, I think you did an okay job given your constraints, but it is by no means the way I'd like to see it done. In fact, I really wouldn't mind seeing this:


    button.onClick() (Event event) {
    // Do something here.
    }


    Now that's clarity and ease of use. Even better would be for me to leave off parameters that I don't care about since I almost never do anything with the event object anyway. Again, though, thanks for doing a reasonable workaround given the language constraints.

    Posted by: tjpalmer on August 28, 2006 at 08:17 AM

  • the only thing that changed is marketing. Back in the good times the language wasn't designed by marketeers, now it is. And because "Ruby has it" (or name your language) "Java should have it too". Whether something makes technical sense no longer matters.

    Posted by: jwenting on August 28, 2006 at 10:33 AM

  • You really want to add over ninety event methods to the AWT and Swing? Then the issue is wading through the even more difficult documentation; these days your IDE helps quite a bit, but back then all we had was Javadoc. That's the problem: all of our options look good as individual examples, but none of them scaled well. No one likes the compromise (grouped events), but the alternatives were worse either in terms of usability or performance. You can see why I'm working in the tools group instead of toolkits these days! :-)

    Posted by: tball on August 28, 2006 at 10:34 AM

  • the only thing that changed is marketing. Back in the good times the language wasn't designed by marketeers, now it is. And because "Ruby has it" (or name your language) "Java should have it too". Whether something makes technical sense no longer matters.

    I can understand your frustration, but your comment is simply not true. None of the four engineers who drafted the closures proposal are in marketing, or were directed by marketing to do anything. Closure support has been requested many times by Java developers and engineering organizations, and the Java language team is responding to those requests.

    Languages in wide-spread use have to evolve slowly, regardless of how frustrating that can be at times. For example, until the latest ANSI C specification many shops were using C++ just because it was a "better C", without using any of its OO support, then years later ANSI C added the most-requested features. I doubt marketing folks had much to do with getting these features into the C specification; sure, C compiler companies were pushing their favorite features, but they had to pass through a rigorous technical evaluation first, with a lot of hot debate over every tiny detail.

    I think that is what is happening now with Java closures, and think that this hot debate by our community will help keep it a first-class language.

    Posted by: tball on August 28, 2006 at 11:00 AM

  • I agree that closures for event handling are ill-advised. They end up making a hodge-podge mess of spaghetti events, since everyone adds only the events they're interested in. I dislike the QT programming model for this reason (and because it requires an additional pre-processing step.

    However I believe closures are extremely useful for algorithm decomposition, as they are used in languages like Ruby. The ability to break down an algorithm into large and small pieces, with clients passing the small in as necessary to perform some task, simplifies a great many things. It also keeps the interesting bits of the algorithm where you want them...in the client code where the rest of your logic lives. Of course collection operations are the most visible example of this, but scoping the lifetime of limited resources is perhaps equally compelling. I say closures are a big win, especially if they're made as clean and easy to use as they are in Ruby.

    Posted by: headius on August 28, 2006 at 12:25 PM

  • Closure is IMHO a "bad idea" (TM of Microsoft Corp ;-) ).

    Sorry, I do not see any serious benefit of introducing this "function oriented" feature into Java language ! This is a feature from the past. There are other java platform languages already existing that can already be used, like kiev.

    I would preffer to see JDK7 add a realy needed features for nowaday softwares like real AOP support (to go further than Java EE 5 new features "early AOP") and improve genericity (make us able to use dynamicity on parameter type for instance) !

    Posted by: bjb on August 29, 2006 at 04:45 AM

  • From the white paper:

    bound method references are harmful because they detract from the simplicity of the Java programming language and the pervasively object-oriented character of the APIs
    Looking at the examples in Neal Gafter's blog, the following snippet
    is surely ugly to read:

    void sayHelloInAnotherThread(Executor ex) {
    ex.execute(new Runnable() {
    public void run() {
    System.out.println("hello");
    }
    });
    }

    But take a look at the proposed/new paradigm:

    void sayHelloInAnotherThread(Executor ex) {
    ex.execute{
    System.out.println("hello");
    }
    }

    Do we really want to obscure the fact that the executor executes a command object, or expect the reader of the code to implicitly assume that a closure conversion takes place behind the scenes? (Please refer to statement 1 - the quote - in this comment)
    Is that a fair price to pay for code elegance / ease of writing code?

    Posted by: bharathch on August 29, 2006 at 05:27 AM

  • bharathch: The closure conversion happens at compile-time. The reader of this code, or any code that invokes a method like ex.execute, may have to refer to the specification of the method to understand what it does.

    Posted by: gafter on August 30, 2006 at 12:19 AM

  • Couldn't have said it better myself:
    bound method references are harmful because they detract from .... the pervasively object-oriented character of the APIs

    Posted by: tobega on August 31, 2006 at 02:00 AM

  • Tom, a great article, and interesting insight into an ancient dispute. Listener interfaces and Adapter classes are a great design. I should point out, though, that it is entirely possible to assign multiple delegates to a single event. This is precisely how delegates work in .NET! Microsoft's multicast delegate design supports an overloaded += operator specifically for this purpose, allowing multiple delegates to be composed as if they were a single delegate. Then, one invocation (the event) dispatches to multiple 'listener' delegates. Multicast delegates also support -= in order to de-register delegates from an event. It all works with anonymous delegates (closures) as well.

    .NET only supports multicast delegates. Microsoft originally intended to include singlecast delegates as well, but decided there was no point. I believe, to this day, there may still be a redundant base class in the delegate inheritance path due to this change of plan. The change originally came too late in the dev cycle for .NET 1.0 to allow full regression testing, so they amended the base class to implement multicast, and left it and the original multicast subclass in. I haven't checked to see if this has been tidied up in .NET 2.0.

    Posted by: cnayoung on April 11, 2007 at 04:18 PM





Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds