One of the promises of closures was that if Java 5 had closures instead of foreach, you could have implemented foreach as a method. Let's put that to the test with the Java 7 prototype.
Firstly, the 'control invocation syntax', which is a little subjective, hasn't been implemented in the prototype, so anything I show here is certainly less than foreach could be with closures.
Here's a simple attempt:
public static <T> void foreach(T[] ts,{T=>void} block)
{
for (int a=0;a<ts.length;a++)
block.invoke(ts[a]);
}
I can call this like so:
foreach(new String[]{"hello","world"},{String s=>System.out.println(s);});
I found that I kept repeating one error in testing out this prototype, namely forgetting the ; for a statement in a {something=>void} closure.
Anyway, the above doesn't work with continue, break or return. Those are not bound yet by the closures prototype. No matter, let's roll our own (ignoring return; I don't know of a way to apply the following technique to return).
Not only can we pass a closure to a method, but the method can pass a closure to our closure! No, I've not yet gone mad:
public static <T> void foreach(T[] ts,{T,{=>void},{=>void}=>void} block)
Perhaps the T,{=>void},{=>void} is better abstracted into a ForeachControl class or something. Imagining that it was, the T field would be called 't', the first {=>void} would be called 'brake' (as in break, but avoiding collisions with the keyword), and the second {=>void} would be called 'cont' (as in continue).
For now, I'm quite happy to use the above. I'll just reiterate it:
{T,{=>void},{=>void}=>void}
is a function type that takes a T, and two {=>void}s and has no return value. A {=>void} is a function type that takes nothing and returns nothing, analogous to Runnable. Here's some example usage:
String[] input={"fish","print","fingers","don't print"};
foreach(input,{String s,{=>void} cont,{=>void} brake=>
if (s.startsWith("fish"))
cont.invoke();
System.out.println(s);
if (s.startsWith("fingers"))
brake.invoke();
});
Ok, that kind of looks like a foreach statement now, plus some baggage for loop control. Actually that's as far as I can go in the current prototype. Let's implement foreach then.
cont and brake both need to 'send a message' to the foreach method, without allowing the closure to finish. Without convoluting the usage code above, I can do that by making cont and brake throw exceptions, which is very similar to how continue and break are planned to be supported in closures, except I'm doing it in-language.
public static <T> void foreach(T[] ts,{T,{=>void},{=>void}=>void} block)
{
class Continue extends RuntimeException { }
class Break extends RuntimeException { }
for (int a=0;a<ts.length;a++)
{
try
{
block.invoke(ts[a],{=>throw new Continue();},{=>throw new Break();});
}
catch (Continue c)
{
continue;
}
catch (Break b)
{
break;
}
}
}
Look at the block.invoke line. I'm passing two closures to block.invoke - one that throws a Continue and one that throws a Break. Other than that, this method is pretty simple.
As I've said elsewhere, even when/if the control invocation syntax appears, you won't be able to implement foreach exactly as in Java 5, because int cannot be a type parameter, and the closures spec doesn't specify any boxing between int and Integer for type parameters.
Even if you never use this code (I won't!), you can see at a small scale the power of thinking in closures, especially for implementing language features. This is one of the reasons why Smalltalk was such a small language - you could implement much of what Java programmers think of as language as library methods. Lisp and FORTH are small languages (at least conceptually - ignore Common Lisp!) for similar reasons. Java 7 is aiming in the same direction.
13 comments:
Nice example, thank you. Keep them coming.
I like how you always write about 'Java 7' although it is not clear yet whether closures will make it into Java 7. I suppose it is a good idea to start speaking about closures and Java 7 in the same context in order to burn this into the brains of Java developers.
Nice posting. One questions: should there be a "void" declaration between <T> and foreach in the original method declaration?
James, you are right. The rest of the code was tested, but I just wrote the first version in my blog without trying it out.
Fixed now. Thanks!
Hi, just one comment: Smalltalk is still a small language! Still alive and running.
Nice article, just had that really weird experience of 'hang on I know that guy' mid way through reading your article, ahhh good old Teleca <g> small world.
No, no, no! We don't need closures!
What is this? I don't understand this language. This is horrible.
@Anonymous:
I suppose that there are many methods in the JDK right now for which you would not understand the implementation at first glance. Fortunately you don't have to understand the implementation, you just have to understand how to use the methods.
This is similar with closures. You will never have to implement a method, which has a closure as an argument. It is enough if you understand how to use the already implemented methods (and using them really is not weird).
Bob,
I don't remember any Bobs from Teleca, I'm afraid, unless you are Bob Sai from Openwave. Could you fully qualify your name please? ;)
Anonymous,
"I don't understand this language. This is horrible.". How long have you spent trying to understand it? There are many languages I don't understand; I don't automatically assume they're horrible.
If you get rid of that assumption, and learn about this, then we might have some meaningful discourse.
Ricky,
Yes, I guess I am fairly anonymous there :).
'Ciaran Jessup' You may still not recognise me, as we only worked together briefly :)
Of course I remember you, Ciaran. You and Andrew Inggs are *the* good programmers from there, out of those who I knew. I used some image loading code that you wrote for years.
It was the "bob" bit that meant nothing to me.
Andy Inggs is to date one of the smartest cookies I've worked with! Damn clever that man :)
Ricky,
Good post once again.
Thanks
Prashant Jalasutram
http://prashantjalasutram.blogspot.com/
The more I look at code like this, the more it reminds me of SML and Haskell.
Post a Comment