Summary
In a recent blog post, Cay Horstmann explains why the double meaning of return - yielding a value and jumping to the end of a method - can cause problems in the context of closures.
Advertisement
In a recent blog post, What's so Taxing about Return?, Cay Horstmann explains that programming languages treat the concept of return differently, with some languages not even having constructs that are equivalent to Java's return statement. While Java's return has been a well-understood concept, the meaning of return can become complicated in the context of the various proposals to add closures to Java, according to Horstmann.
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...
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 contains 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...
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 no return (or break or continue) to worry about.
Horstmann notes that the problem also stems from the fact that developers can use closures both as control structures and as a form of callback:
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.
Those opposing the introduction of closures to Java often cite the increasing complexity that closures would bring to the language. While closures in other languages, such as Ruby, have found their natural expression, and few developers seem to be confused by them, Horstmann's post shows that closures can in some cases make the meaning of code ambiguous.
Do you support adding closures to Java? Do you believe developers will find the best use-cases for closures without getting confused by the double role closures can play in Java?
I absolutely oppose the addition of closures to java. It is an overly complicated and redundant feature that would serve only to frighten and confuse us.
I have proposed an inner class/closure extension to Java and I realize that your comment isn't meant seriously. But the proposals are really just inner classes in disguise. Your example in my C3S proposal would be:
PS I am guessing you didn't really mean to call compareTo but to put the comparison code in. If it already implemented Comparable you wouldn't need to pass a Comparator. Note once you spell out longer code, the syntactic sugar looks less valuable :(
>Note once you spell out longer code, the syntactic sugar >looks less valuable :(
Absolutely. That's why I am unmoved by closures. If you are going to write real-world comparators (to follow the example in this thread) write them as a class so they can be reused, extended, DIPped, etc. And so that they are in ONE place, not many.
I'm afraid that closures will encourage sloppy, hard to maintain, cut-and-paste programming. And when you suddenly change your Comparator to handle Chinese, some new Currency, some new statistical test, or the new Daylight Savings Time, you will have dozens of little closures to fix, instead of one class.
Yeah, yeah. I was just blasting out an example of what I consider absurd code in java. So how about we sort the array of strings via length, if it allows us to get past the nitpicks and focus on the horrifying syntax:
which is a variation on the C# syntax. The backslash and arrow are as close as we could come to the lambda/right arrow notation used in most CS programs when discussing lambda calculus.
Getting back to the articles point, I don't thing "return" in a closure should mean what most people apparently think it should mean. I think it should mean: return from this closure, *not* from the block where the closure was defined. After all, presumably closures are first class objects and can be assigned to variables, class fields, or returned out of the function to be invoked later.
Given that that's the case, a return statement in a closure can be invoked at any time, even long after the function in which it was defined actually returned. It's the upward funarg problem, whereas it seems like everyone is simply discussing the downward funarg problem.
Ruby supports returning out of blocks (which quasi-enforce downwardness), but I can't understand why. Every time I see examples using it, I just think "why are they using each() instead of find()?"
Anyway, I'm perfectly willing to admit I'm stupid and may be missing something basic here, but I would point out that we have closures in our language, with a sane syntax, working right now as described above. So, while I may be an idiot, I'm an idiot with working closures.
At some level, I do hope java stays true to its roots and doesn't add closures. It makes it that much more likely that some other high-level language will come along that people get excited about.
> Getting back to the articles point, I don't thing "return" > in a closure should mean what most people apparently think > it should mean. I think it should mean: return from this > closure, *not* from the block where the closure was > defined. After all, presumably closures are first class > objects and can be assigned to variables, class fields, or > returned out of the function to be invoked later.
I am also kind of leaning towards return not exiting from the enclosing method. I think there's going to be confusion either way and it makes things a little confusing. If you call a map function with a closure and you return from the closure and that returns from the enclosing method, does that also mean you've shortcut the map method? That's kind of weird.
On a side-note I want something similar in Scala. I was trying to write something a little weird and I found that I really wanted to yield from inside the closure. I won't pretend to know whether this should be easy to implement or not but from a developer standpoint, I think it would be really slick and make some kinds of code much more clear and concise. Yield is also different because it doesn't stop the execution of the method.
Agreed (obviously). I think the argument goes away the moment you realize you can pass closures out of the method they are created in, either by returning them or setting them to a field on an object somewhere. At that point, any other semantics for return become insane.
It's easy to get caught up in only thinking about the downwards funarg semantics. Maybe this is because Ruby goes out of its way syntactically to make closures/blocks almost always downwardish and a lot of use cases are downwardish. It isn't the whole story though: callbacks for example are almost always upward.
It is unfortunate that the language around closures/blocks/lambda statements is so academic. They really are practical and beautiful with tasteful application.
Yes. Wonderful. Then when your boss says "gee, v 2.0 sorts by length, but version 2.1 supports Japananese, when we convert to Kanji and sort by something else"
If you have used closures a lot, it's a maintenance nightmare. If you have used them only a little, why should Java add confusing (to some) syntax for something you don't use much?
Current Java, by semi-forcing you to write an actual Comparator, makes the code much more maintainable.
With regard to the sorting example, and supposing that we weren't allowed to change the closure classes then in C3S you would write.
sortBy foo method( str ) { str.length }
One of the purposes of keeping C3S as simply syntactic sugar is that you can then refactor, for example when the search criteria changes. Hopefully identical comparisons would already be factored out, but if not:
Then you can change string sorter. The idea is to support this sort of re-factoring. This in the main reason why I want anything new to simply be an inner class; not a new, but similar construct, with new semantics. This brings me onto the non-local return.
First I need to say that I don't know of a good use for non-local return, but it is feature others think important hence it is in C3S. However because its behaviour is open to abuse I have suggested that it is clearly marked, i.e.:
Return should return from the block, not from the current method. If the programmer wants to return from the current method, he can set up a condition to return if required. Keeping things clean is very important.
We are gonna have to agree to disagree brother. You can always centralize the sort closure somewhere as a constant if you want to reuse it, and refactoring tools should be able to detect duplicates and suggest replacement. It's all just code.
In my experience, people get object models so wrong, so frequently (myself first and foremost) that, in the long run, it's always a rewrite, so we might as well make the rewrite fast.
Return should return from the block, not from the current method. If the programmer wants to return from the current method, he can set up a condition to return if required. Keeping things clean is very important.
> > Compare and contrast with ... > > > foo.sortBy( \ str -> str.length() ) > > > Yes. Wonderful. Then when your boss says "gee, v 2.0 > sorts by length, but version 2.1 supports Japananese, when > we convert to Kanji and sort by something else"
I can see where you are coming from but it's also the case that things that are very specific don't warrant a full separate class. I've seen code bases where a lot of trivial things were separated into their own classes. You end up with so many of them that actual reusable classes are lost in the noise and are written multiple times.
If you only do the above once, then it really doesn't matter if the requirements change. You only have to makes changes in that one place. If you are using it more than once, then yes, you should definitely consider creating a separate reusable class. Closures don't prevent this any more than interfaces force you to do it.
Having said that, I'd say that I'm a little unmoved that closures will be a great improvement in Java. The fact that there are so many different ideas about how they could or should work is not a good sign. Personally, I'd rather they just add function pointers (perhaps using synthetic interfaces.) This would greatly improve the reusability of existing classes without the complexity of closures.
I think one of the dangers with some of the closure proposals, i.e. FCM and BGGA, is that the closure isn't an inner class. Therefore if you start of with a closure and refactor to an inner class you might introduce hard to find bugs. Both C3S and CICE support full inner classes and therefore no problem with these.
As an aside FCM and BGGA do not give access to the inherited this pointer inside the closure, only to the enclosing this pointer. Thus if there is an anbiguous reference, e.g. a feild of method that could come from either the enclosing scope of the inherited scope, then an inner class will give you the inherited scope and the closure the enclosing scope. Thus you get a tricky to find problem when you refactor. Also explaining why one construct goes one way and the other does the opposite will be a nightmare - think queues outside of the Prof's office :(
> I can see where you are coming from but it's also the case > that things that are very specific don't warrant a full > separate class. I've seen code bases where a lot of > trivial things were separated into their own classes. You > end up with so many of them that actual reusable classes > are lost in the noise and are written multiple times.
Agreed. I too dislike projects with hundreds of one-use or trivial classes. As a quasi-fix to cut down on the number of source files, I use named inner classes a lot. For example, create a class called Comparators, then put a bunch of comparators in there as inner classes (or statics). Same for Enums. At least makes them easy to find.
Flat View: This topic has 15 replies
on 2 pages
[
12
|
»
]