Skip navigation
ANNOUNCEMENT: community.oracle.com is currently Read only due to planned upgrade until 29-Sep-2020 9:30 AM Pacific Time. Any changes made during Read only mode will be lost and will need to be re-entered when the application is back read/write.

??? The Dual Role ofreturn

Many programming languages get along just fine without areturn statement. In Pascal, for example, the return value is assigned to a dummy variable whose name equals the name of the function.

function foo(arg: integer): integer;
begin
   (* compute return value *)
   foo := retval;
   (* maybe some more cleanup *)
end;

In Scheme, the body of a function is a sequence of expressions, and the value of the last expression is returned as the result of the function call:

(define foo (lambda (arg) expr1 expr2 .... retvalExpr))

And, of course, in assembly, the return value is simply deposited in a register :-)

movl retval, %eax

In Pascal and Scheme, you return to the caller when you reach the end of the function. There is no equivalent to the “quick and dirty”

if (somethingAbnormal) return null;
// more work in the normal case...

This shows that there are really two aspects of returning:

  • what to return
  • when to return

???Closures

A closure is a block of code, packaged up for execution at a later point. When it executes, all the references to the surrounding code should just work as if the code had executed in the defining scope.

Here is a typical example, using the BGGA 0.5 syntax (which, like all proposal syntax, is highly subject to change):

public static void main(String[] args) {
    JFrame frame = new JFrame();
    JButton button = new JButton("Click me!");
    frame.add(button);
    int counter = 0;
    button.addActionListener({ ActionEvent e => 
        counter++; 
        frame.setTitle("Clicked " + counter + " times."); });
    frame.pack();
    frame.setVisible(true);
}

When the button is clicked, the closure gets called, the counter variable in the enclosing scope is updated, and the frame title is set to reflect the click count.

Wait, there is a problem here. By the time the button is clicked, main has terminated and the local variablecounter is dead and gone.

Actually, though, the closure will capture a reference to anew int[1] containing the counter, and of course, a reference to the JFrame object.

All this can be done with any of the various closure proposals, by gussying up inner classes with the ability to capture non-final locals.

Unlike some other closures proposals, BGGA goes further and says that the closures also need to capture the meaning of execution transfer statements, i.e. break, continue, andreturn. (What about throw? That's never statically typed, so we don't expect to capture it.)

At first glance, this seems like an odd thing to do. When the action listener executes a break statement, surely we don't want to go back in time and revive the main method (at least not until the proposal to add continuations to Java :-))

But it comes in handy for another use of closures: programmer-provided control statements. Let's say I want to provide an easy way of iterating over a matrix:

for each(int i, int j: matrix) { // look, ma, no matrix[i].length!
    if (matrix[i][j] == 0)
        continue;
    . . .
}

This actually means: Pass matrix and the closure

{ int i, int j =>
    if (matrix[i][j] == 0)
        continue;
    . . .
}

to the each method:

public static void each(int[][] a, { int, int => void } block) {
    int i = 0;
    int j = 0;
    for (; i < a.length; j = (j == a[i].length ? 0 : j + 1), i = (i + ((j == 0) ? 1 : 0))) {
        block.invoke(i, j);
    }     
}

Of course, now continue should mean the right thing: continue after the block, with the next iteration of thefor statement. (Sorry about the tortured logic in thefor update; one must use an assignment, increment, method call, or new expression. I suppose I could use a closure invocation { => if (j < a[i].length) j++; else { i++; j = 0; }}.invoke()...)

???The Point of No Return?

Back to the topic of returns. A closure returns a value (if it has a result type). For example,

{ int x, int y => Math.max(x, y) }

returns an integer, the max of its parameters. But if a closure contais a return statement, that means to return from the enclosing block. For example,

{ int x, int y => return Math.max(x, y); }

is a closure with return type void that, when invoked, causes its caller to return the max of the parameters (or, presumably, if the caller can't return an int, throw an exception).

Several commentators to my earlier blog point to this issue as the Achilles heel of the BGGA proposal.

More unhappily, the closure

{ int x, int y => Math.max(x, y); }

computes the max of its parameters, discards it, and returns no value.

When I heard about that, my gut reaction was fear...the fear of students queuing up for my office hours.

Ultimately, the culprit is the dual nature of return: yielding a value, and jumping to the end of the method code. In Pascal or Scheme, none of this is an issue. These languages have noreturn (or break or continue) to worry about.

???Many Happy Returns

Let's try to throw some syntax at this. A BGGA closure body is a sequence of statements followed by an optional expression. Maybe that's too subtle. Let's make the return expression more prominent. Something like

{ int x, int y => int : stats => Math.max(x, y) }

I already see the line outside my office getting shorter. (I suppose it would also allow early returns from a closure, but I don't want to go there...That's what got us in trouble in the first place.)

But I have to agree with Stephen Colebourne that there are two entirely separate use cases here.

When one uses a closure for a control abstraction, the return type must be void since the closure denotes a statement. And it is pretty clear that return means to return from the enclosing scope.

When one uses a closure as a callback, to be invoked at a much later time, does one ever want to capture the enclosing semantics of return? I don't think so, but I will find out soon enough if I am wrong...

It would make sense to differentiate these use cases.

In a control abstraction, the programmer doesn't provide an explicit closure, but the compiler puts together a parameter list and a block. Conversely, when passing a closure to a callback, the programmer does the { ... => ... } thing. So, we can tell them apart.

In the first case, the block can contain return,break, continue, labeled break, etc. Pile it on!

In the second case, none of them should be allowed. It's just a syntax error. That should take care of the line outside my office. Students can wrestle with the compiler—what gets them is code that compiles and does the wrong thing.

This is almost the same as the RestrictedFunctioninterface, except that you can capture non-final locals. It is also somewhat related to the distinction in BGGA control abstractions. The for control abstractions allow break andcontinue, whereas other control abstractions don't.

If I understand the FCM/JCA proposal correctly, they have essentially the same solution. But the meaning of returnchanges from one use case to the other. I am not sure that's such a good idea. But again, it's just syntax.

I am a total amateur at this, of course, as I and the world are sure to find out from the blog comments in a few hours. But it seems to me that after the tweaks that are sure to come, BGGA will differ very little from FCM/JCS, except for the issue of method literals. (These may be nice to have for other purposes. I'll warm up to them if someone can show me how they solve my pet peeve: property boilerplate.)

I teach a graduate programming languages class at San Jose State University. In order to inject some topics of current interest, I had a lab about closures and the competing closure proposals for Java 7. I got an email from Neal Gafter: “Hey, it's really cool to see your reference to BGGA in a SJSU lab assignment!” I asked if he could give a talk at the department seminar, to which he graciously agreed. We had a packed room today. I am very excited that my students had a chance to witness a bit of history in the making. Here are my impressions of the talk.

The End of History - Not.

I have taught CS252for many years. I cover the lambda calculus, closures, continuations, metaprogramming, and other advanced programming language topics. For several years, my graduate students viewed the material as having little relevance to their lives. Clearly, real programming languages had evolved from C to C++ to Java, and the end of history had been reached. Every other language was either some poorly designed pile of crud or an ivory tower obsession.

I tried what I could, talking about continuations for web applications and Paul Graham's article on Lisp in a startup. My students, however, are by and large a pragmatic bunch. They know that web applications aren't written in Lisp, and googling for Paul Graham led them to this funny but unflattering article.

This year, things were finally going my way. The buzz behind Ruby and Rails is hard to ignore, and it seems pretty clear that Java will get closures in some form or other. Learning about metaprogramming and closures doesn't seem a waste of time any more.

How Not to Write a Proposal

Alex Miller has a nice blog on Java 7 proposals, including thecompeting closure proposals. My students found the CICE proposalpretty straightforward. It starts out with a simple promise:

For example, here is the Java 5 code to start a thread whose run method invokes a method named foo:

    new Thread(new Runnable() {
        public void run() {
            foo();
        }
    }).start();

If we adopt this proposal, the following code would be equivalent:

 new Thread(Runnable(){ foo(); }).start();

The first impression is that this is easy and convenient.

In contrast, the BGGA proposal starts out with:

Modern programming languages provide a mixture of primitives for composing programs. Most notably Scheme, Smaltalk, Ruby, and Scala have direct language support for parameterized delayed-execution blocks of code, variously called lambda, anonymous functions, or closures. These provide a natural way to express some kinds of abstractions that are currently quite awkward to express in Java. For programming in the small, anonymous functions allow one to abstract an algorithm over a piece of code; that is, they allow one to more easily extract the common parts of two almost-identical pieces of code. For programming in the large, anonymous functions support APIs that express an algorithm abstracted over some computational aspect of the algorithm. For example, they enable writing methods that act as library-defined control constructs.

I translated this into plain English in my lecture:

We are smarter than you are.

The proposal then goes on to discuss closure literals, function types, closure conversions, exception type parameters, definite assignment, the type Unreachable and control invocation syntax. That section finally contains a compelling example:

with(FileReader in : makeReader()) with(FileWriter out : makeWriter()) {
    // code using in and out
}

No more try/finally/in.close. Hooray!

When we discovered that BGGA gives us this and CICE doesn't, we were getting much more excited about it.

The Presentation

Neal told me before the talk that he has given this presentation fifteen times already—someone ought to make a movie about him tirelessly criss-crossing the planet and spreading the message :-) Actually, you can see a video of the presentation here.

He started out with this quote:

"In another thirty years people will laugh at anyone who tries to invent a language without closures, just as they'll laugh now at anyone who tries to invent a language without recursion." - Mark Jason Dominus

I am not sure whether my students understood this—they had probably never seen a language without recursion. Sure, I tell them in the undergraduate programming languages class about Fortran, but they view it as a freakish thing, like a two-headed cow, not as something that was taken seriously in its time. But Neal went on to say that one now says the same thing about garbage collection, and my students do remember the dark ages of C++ and having to calldelete (but not too often).

Neal systematically defined closures, discussed why anonymous instance creation expressions fell short, defined the requirements for a true solution, went over syntax, and finally presented the examples. The talk is much easier to follow than the spec, and everyone enjoyed it tremendously.

Still, if I had given the talk, I would have given the examples first. Since this is my blog, I get to do it my way, so here goes.

Closing Locks, Files, Database Connections

Don't you hate writing this code?

Connection conn = dataSource.getConnection();
try {
   . . .
} finally {
   try {
      conn.close();   
   } catch (SQLException ex) {}
}

On a bad day, I even worry about a SQLException that triggers the finally clause, only to have a second exception occur in conn.close() that masks the first one.

With BGGA closures, you write

with (Connection conn : dataSource.getConnection()) {
   . . .
}

Iterating over Maps

I can't ever remember whether it is

for (Map<String, Integer>.Entry entry : map.entrySet()) {
   String key = entry.getKey();
   Integer value = entry.getValue();
   . . .
}

or

for (Map.Entry<String, Integer> entry : map.entrySet())

I either look it up in Core Java or, if I am lazy, I sheepishly fall back on

for (String key : map.keySet())

and feel bad about paying for the additional get.

With BGGA, I can have

for eachEntry(String key, Integer value : map) {
   . . .
}

I want that.

For that matter, I want to iterate over a two-dimensional array as

for eachIndex(int i, int j : matrix)

without abominations such as a[i].length. I also want to have both index and value when iterating over a list. With BGGA, I can make control structures for those purposes.

Callbacks

All the closure proposals handle this use case, but here goes, for the sake of completeness:

myButton.addActionListener({ ActionEvent event => model.myButtonAction(); });

It seems a shame that one has to do something with the uselessActionEvent. Ideally, I'd like to be able to ignore the parameter and write

myButton.addActionListener({ => model.myButtonAction(); });

But maybe it's asking too much.

Logging Exceptions

Neal didn't talk about this, but I gather from the spec that this must be possible.

I hate writing reflection code like this:

try {
   m.invoke(arg1, arg2);
}
catch (IllegalAccessException ex) {
   logger.log(Level.SEVERE, "", ex); 
} catch (IllegalArgumentException ex) {
   logger.log(Level.SEVERE, "", ex); 
} catch (InvocationTargetException ex) {
   logger.log(Level.SEVERE, "", ex); 
}

If I understand the proposal correctly, I can instead write

<throws IllegalAccessException|IllegalArgumentException
      |InvocationTargetException>.tryAndLog(logger) {
   m.invoke(arg1, arg2);
}

It's still not pretty, but I can't fault BGGA for a cumbersome API with a method that throws three unrelated exceptions.

Process and Politics

Neal talked a bit about the process of getting a language extension accepted for the Java language. The JSR process seems rather company-centric and doesn't seem to deal all that well with the possibility that two people from the same company (in this case, Neal Gafter and Josh Bloch, both of Google) have differing views. Still, he hinted that something will happen in the near future about getting a JSR on closures started.

Overall, my impression was that Neal's approach is right. You want to be able to take a block of code, wrap it up and hand it to some other code and just have it work. Anonymous inner classes won't do it, no matter how much syntactic sugar you sprinkle over them. When evaluating the competing proposals, I suggest that you look at the use cases first before you get flummoxed by the syntax (which may well change) and the arcane considerations of definite assignment.