 |
Closures and Multiple Return Values
Posted by brucechapman on November 28, 2007 at 08:00 PM | Comments (14)
In javaposse 150 the guys were talking about something and veered off on a tangent talking about multiple return values and tuples and such like. Next up they talked about the closures JSR. That sparked a thought that maybe we could transform the multiple return values problem in order to solve it using closures.
So what if instead of returning multiple values, a method returned void, but had another formal parameter which was a closure to receive the results of the method?
I've downloaded the closures prototype compiler (2007-11-20 version) and had a play with that idea. Now if it had been a complete disaster you wouldn't be reading this, because I wouldn't have blogged about it, but you are reading it so lets get on with it.
Here is a method to convert rectangular to polar coordinates and pass the results (r,theta) to a receiver closure.
static void toPolar(double x, double y, {double, double => void } rThetaReceiver) {
rThetaReceiver.invoke(Math.sqrt(x*x + y*y), Math.atan2(y, x));
}
And this is how you use it
// test toPolar
toPolar(3,4,{double r, double theta =>
System.out.format("3,4 = %f<%fRad%n",r,theta);
});
Here is the complete class with both converters, and a round trip test. Note how the round trip nests the closures.
package closures.multireturn;
/**
*
* @author bchapman
*/
public class RectangularToPolar {
static void toPolar(double x, double y, {double, double => void } rThetaReceiver) {
rThetaReceiver.invoke(Math.sqrt(x*x + y*y), Math.atan2(y, x));
}
static void toRectangular(double r, double theta, { double, double => void} xYReceiver) {
xYReceiver.invoke(r * Math.cos(theta), r * Math.sin(theta));
}
public static void main(String[] args) {
// test toPolar
toPolar(3,4,{double r, double theta =>
System.out.format("3,4 = %f<%fRad%n",r,theta);
});
// test toRectangular
toRectangular(5, Math.PI / 2, { double x, double y =>
System.out.format("5<90deg = %f,%f%n",x,y);
});
// test Round trip - toPolar and back to rectangular
toPolar(3,4,{double r, double theta =>
System.out.format("3,4 = %f<%fRad%n",r,theta);
toRectangular(r, theta, { double x, double y =>
System.out.format("%f<%fRad = %f,%f%n",r,theta,x,y);
});
});
}
}
And here's the output
C:\projects\quickhacks\src>javac closures\multireturn\RectangularToPolar.java
C:\projects\quickhacks\src>java closures.multireturn.RectangularToPolar
3,4 = 5.000000<0.927295Rad
5<90deg = 0.000000,5.000000
3,4 = 5.000000<0.927295Rad
5.000000<0.927295Rad = 3.000000,4.000000
This looks promising. I'll keep exploring.
One of the interesting things which I think this demonstrates is how closures are such a fundamental building block that they offer the potential of solving quite a few other problems that we've traditionally thought of as requiring specialist language changes. Maybe such solutions are not quite as nice as a specialist language change, but good enough that the advantage of not having yet another language feature outweighs the now small disadvantage of not having the perfect solution.
Bookmark blog post: del.icio.us Digg DZone Furl Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment
-
@patrikbeno
I should have been more clear -- these are obviously closures, but what he's doing is implementing a "continuation-passing style" of programming using closures. It's a standard insight, that using continuations (or programming in a style as if you had them) lets you do things like "return multiple values."
They're not "completely different concepts" -- it's using a first-class function (a closure) as a roundabout way of naming the control state of your program. Most people would recognize this as "implementing continuations."
Posted by: arthegall on November 30, 2007 at 08:05 AM
-
Constructions like Generics and Closures could make sense if implemented correctly (by that I mean prioritise elegance over backwards compatibility). But very domain specialized language features like LINQ or native XML types, I considered to be *very* bad for a general purpose language like Java. On the other hand, I also tends to take a very simplistic (dogmatic) approach to language design (always remember the KISS principle!). I would rather write a couple of extra lines of code rather than try to remember a lot of arcane language constructions and their semantic - that's why I never really liked C++!.
Posted by: jesperzuschlag on November 30, 2007 at 04:29 AM
-
I think this is not good for the language, java could end up like another c++, where by newbies are completely swamped by the number of language features. The great think about java is that it is easy to pick up and use. Is closures actually coming to java, if so I think it is a sad day. I hope we don't go the c++ way and be left behind due to bad hacks to bring in new features.
Posted by: cormacmul on November 30, 2007 at 01:27 AM
-
Java doesn't need closures.
Java could use LINQ like C# has though; that would be pretty damn cool.
I think if I ever need functional programming on a JVM that I'll use Scala, or as in my previous post, I'll provide a callback object in the rare (very rare) case that I ever need to do this.
Posted by: naikrovek on November 29, 2007 at 09:08 PM
-
No you are not. I also thing that this stuff is a very bad idea.
For all values of "thing" that approximate the meaning of "disagree".
What values of "thing" make your statement true for you?
Posted by: brucechapman on November 29, 2007 at 02:06 PM
-
Am I the only one to thing that this stuff (namely having function oriented programing in Java) is "A VERY BAD IDEA" ?
Am I the only one to thing that runtime aware genericity and generalized annotation
Posted by: bjb on November 29, 2007 at 01:55 PM
-
Just return a java.util.Map with the two values:
Map res = new HashMap(); res.put("x", ...); res.put("y",..); return res;
In that way you can simulate tuples of functional programming.
If you don't want to create a class just for returning some values.
I agree that it creates confution to use parameters as output.
Posted by: aldo_gg on November 29, 2007 at 01:27 PM
-
naikrovek: why not just provide a callback object to the method returning void?
That is what is happening, except with closures the callback object looks more like a simple block of code, that is part of what closures do.
But yes, you could do the same thing with a callback object, but you'd need to define the interface and the anonymous inner class and all that baggage makes the solution impractical. With closures it becomes practical.
varan: Apart from convenience ... is it all there is to this much ballyhooed 'improvement'?
That depends what you mean by "this ... improvement". If you mean closures, then definately NO, there is much more to it. If you mean using closures as an alternative to multiple return types, then yes the only value this offers over a specialist return value class (either inner class or not) is convenience. Sometimes that is appropriate, sometimes not. As the javaposse discussed, for an API it is better to have a special return class to bundle up the multiple values. However there are cases which are internal to a compilation unit where you just need a simple convenient way to pass back multiple values. My argument is that closures enable a good enough mechanism for those cases, and therefore we don't need multiple return values as a language feature to address those cases.
rhasselbaum: Possibly you can't tell which is the output parameter from looking at the signature because the idiom is not yet an idiom. However once you become familiar with it then it is quite obvious from the signature
static void toPolar(double x, double y, {double, double => void } rThetaReceiver)
that the closure called rThetaReceiver is going to receive r and theta. If this mechanism becomes a common idiom, and this use of a closure becomes known as a "receiver" (or somesuch consistent name), then the confusion is eliminated. I've got a couple more blogs in the pipeline (my head for now) on this topic which may illuminate things more.
Posted by: brucechapman on November 29, 2007 at 12:50 PM
-
You're forgetting one of the points the JavaPosse guys raised (and I agree with): Using parameters for output is sketchy. It creates confusion for others looking at the code: What parameters are inputs? What parameters are outputs? I can't tell from looking at your signature. Calling this method has a side-effect that's not apparent without reading the implementation.
Posted by: rhasselbaum on November 29, 2007 at 10:59 AM
-
Apart from convenience, the example here do not provide any other value that instances of inner classes (which are 'cleaner' albeit more verbose)
do.
Is it all there is to this much ballyhooed 'improvement'?
Posted by: varan on November 29, 2007 at 10:21 AM
-
why not just provide a callback object to the method returning void if you need to notify something that you're done and/or provide results of an unknown type? That's what I've always done (in the rare case when I've needed to do it) and it has always worked well for me.
Posted by: naikrovek on November 29, 2007 at 06:45 AM
-
Thanks for sharing your experiments. I see how closures enable this language feature. After having closures in the language, a syntactic sugar for multiple return values is not far fetched.
Posted by: fatihc on November 29, 2007 at 01:31 AM
-
@arthegall:
no, these really are closures :-)
continuation is a completely different concept (kinda like serializing your thread of execution at some point and resuming it later)
but there's no point in explaining that since you have Wikipedia URLs for both :-)
Posted by: patrikbeno on November 29, 2007 at 12:49 AM
-
These are called continuations, usually.
Posted by: arthegall on November 28, 2007 at 09:01 PM
|