Stephen Colebourne: Function types, or method types as FCM refers to them, are one of the most controversial features of closures. Is there an alternative that provides 80% of the power but in the style of Java?
Stephen went on to derive an alternative syntax for the part of BGGA closures proposal that's hardest for ordinary Java programmers to "get".
To understand what Stephen's talking about, look again at my Friday Java Quiz closure example 20 days ago:
public class Fib {
private static {int=>int} fib = {
int n =>
n==0 ? 0 :
n==1 ? 1 :
fib.invoke(n-1) + fib.invoke(n-2)
};
public static void main(String[] args) {
System.out.println(fib.invoke(6));
}
}
Knowing that most ordinary (by which I mean non-new-language-feature-designing) Java programmers are not following the closures development, I put this example on the white board in my office and quizzed everyone walking by. Of the six or seven extremely bright people who were put on the spot by my question "Come here. Take a look at this and see if you can figure out what this program means" only Rob deciphered the program without any hint.
There are three difficulties associated with this piece of code:
The function type syntax (the red part)
The input variables specification syntax (the blue part)
The invocation syntax (the green part)
Of the three, the function type syntax is the most severe cognitive barrier. Most of my innocent victims simply get stuck at the notation and can't parse any further. I have to draw a black box around it and say "This is a type" before they can make sense of the rest of the example. They'll then say "Oh, so fib is a function that take an int and returns an int. Which is the input and which is the output?" Or "Why don't we name the input variables and the output variables in the function type?" David even asked "Why can't we have more than one output variables?"
Those who learned Ruby closure syntax quickly recognized the resemblance between the "{ int n => ... }" and Ruby's "{ n |... }" . But some reacted violently to the newly coined token "=>". Brian is offended by it enough to come up with his own alternative using the # character instead.
The problem with ".invoke" are revealed by questions like "The .invoke() must be a method of fib. Where is it defined?" At which point an explanation must be given about the generated interface for function types and its invoke() method. Brian went so far as to erase the ".invoke"s from the example. And I do agree that the code looks more natural without them.
Mark's reaction to my quiz included the question about what can go legally into the right hand side of the "=>" in a closure literal. You can't guess it by looking at my example, but a sequence of zero or more statements terminated by semicolons and an optional final expression without the terminating semicolon can go there. And the final expression will be the value of the closure invocation expression.
As long as attitude towards closure proposals are concerned, I'm still seeing mainly two camps: the "I've been programming in Java for ten years, and never once were I wishing Java had closures" camp, and the "If we are going to add closures to Java, which seems inevitable at this moment, let's do a full fledged one like the BGGA closures" camp. There are some switch overs from the first camp to the second camp. Amazingly, nobody is switching from the no-closures camp into the other lets-do-a-little-bitty-syntax-sugar-here-and-there camps.
My personal experience with trying out the BGGA closures prototype is very similar to those of my victims: puzzled a little at first, and one by one, the concepts clicked. And the whole thing doesn't feel that crazy as my first impression suggests. Unlike Brian, I had no problems with the "=>" notation. Maybe it has something to do with my mathematics background where we use "=>" or "->" to mean exactly what the BGGA closures proposal means. For example:
floor: R ⇒ Z
floor(x) = max { n ε Z | n ≤ x }
However, I do see points in Brian's and now Stephen's proposal. I like Brian's proposal better because it doesn't introduce a new name for the type. Stephen's proposal is like a typedef, where many names could be introduced to represent the same function type.
I do have a better solution. Instead of inventing a new, different way of representing function types, why don't we borrow the notation from a language where functions are first class entities. No, I'm not talking about Haskell or ML or even Scheme. I'm talking about C. Yes, C, as in, the C language where Java inherited a whole bunch of basic syntax.
For those who are not familiar with C, a variable in C can be of a type of a function. And functions can be arguments and return types of other functions. And those three places are exactly where the BGGA closures needs syntax for function types. So why don't we borrow the C syntax with some simplifications.
To designate a variable or field as of the type "{int=>int}", we simply say:
int (*fib)(int);
compare with:
{int=>int} fib;
To designate that a method takes a function type parameter, we simply say:
int apply(int (*fib)(int), int x) {
return fib(x);
}
compare with:
int apply({int=>int} fib, int x) {
return fib.invoke(x);
}
Similarly for returning function types:
int (*addn(int))(int) {
// ...
}
compare with:
{int=>int} addn(int) {
// ...
}
To initialize a field or a variable with the closure literal, we can say:
int (*fib)(int n) {
n == 0 ? 0 :
n == 1 ? 1 :
fib(n - 1) + fib(n - 2)
}
compare with the difition at the beginning of the post.
Notice all of the above are valid C code and should make Java programmers feel more at home.
If we want to go further along this direction, we can define a closure literal with the following syntax:
(*)(int n) { n * n }
If the thought of using the "*" clouds your mind, we can use "#" in its place.
A caveat: I'm not a language designer, and I'm not proposing anything here. What I said may or may not make sense. To quote Dennis quoting Dennis Miller, I could be wrong!
But I don't like the idea of Stephen Colebourne having all the fun! ;=)