Closures and the return of the return

I attended Joshua Blochs’ presentation on closures at JavaPolis last week (watch the video here). This slides about return not return from what you’d expect kept me wondering: how do other languages solve this ‘problem’.

The example from Bloch, taken from Slide 38 of Blochs’ closures controversy presentation:

[java]
static Boolean contains(Iterable seq, {E => Boolean} p) {
for (E e : seq)
if (p.invoke(e))
return true;
return false;
}

static Boolean test() {
List list = Arrays.asList(
‘h’, ‘e’, ‘l’, ‘l’, ‘o’);
return contains(list, { Character c => return c > ‘j’; } );
}
[/java]

The point Josh tried to make was that due to this ‘copy and paste’ error the contains method would return on the first invocation of the passed block. Auch….

It asked around at Morning coffee but nobody really knew what this would actually do in Ruby.

So, let’s just try it. In Ruby this might look a bit like this:

[ruby]
def contains(enumerable, &test)
enumerable.each do |e|
return true if test.call(e)
end
false
end

puts contains(['h','e','l','l','o']){|v| return v == “z”}
[/ruby]

In the Ruby a return statement in a block returns from the enclosing function as well. However, if the return statement is part of a closure passed to a function returning is not allowed:

closure_return.rb:9: unexpected return (LocalJumpError)
from closure_return.rb:4:in `call’
from closure_return.rb:4:in `contains’
from closure_return.rb:3:in `each’
from closure_return.rb:3:in `contains’
from closure_return.rb:9

Throwing an error might be considered a better solution. The problem in this case is the fact that the error is thrown at the first invocation of the block; code before the invocation of the block will be called… but code after the invocation won’t. Of coarse this is ‘just’ a matter of taking this into account:

[ruby]
def contains(enumerable, &test)
begin
puts “a”
enumerable.each do |e|
return true if test.call(e)
end
rescue
puts “Error in passed test-block: #{$!}”
ensure
puts “b”
end

false
end

puts contains(['h','e','l','l','o']){|v| return v == “z”}
[/ruby]

In this case the ensure statement will always be called. I’m not really in favor of adding closures to Java; just go to Ruby/Groovy/Scala if you want them). I do however think that the ‘return’ argument Josh made isn’t to strong; a solution similar to the way it works in Ruby could probably be found for Java.

This entry was posted in closures, java, ruby. Bookmark the permalink.

13 Responses to Closures and the return of the return

  1. nmgafter says:

    Josh’s code is and will continue to be a compile-time error in BGGA closures.

  2. jbloch says:

    I believe that Neal is mistaken. Here is the entire program:

    import java.util.*;
    
    public class Junk4 {
        static  boolean contains(Iterable seq, {E => Boolean} pred) {
            for (E e : seq)
                return true;
            return false;
        }
    
        static boolean test() {
            List list = Arrays.asList(
                'h', 'e', 'l', 'l', 'o');
            return contains(list, { Character c => return c > 'j'; });
        }
    
        public static void main(String[] args) {
            System.out.println(test());
        }
    }
    

    When compiled under the most recent BGGA prototype (closures-2007-11-30), it generates a single compiler “error”:

    c:/jjb/bgga/closures-2007-11-30/bin/javac Junk4.java
    Junk4.java:14: Nonlocal return not yet implemented.
    return contains(list, { Character c => return c > ‘j’; });
    ^
    1 error

    In other words, the prototype BGGA compiler thinks the program is fine. (Of course the compiler doesn’t know or care how confusing it is.)

    Josh

  3. jbloch says:

    P.S. Sorry about the lost formatting. I guess I should have put in some tags.

  4. peter says:

    I’ve added some tags to the post to fix the formatting.

  5. peter says:

    The error message generated by the BGGA compiler does indeed suggest that non-local returns will be allowed in the future; but it something like this will probably be open for debate.

  6. nmgafter says:

    Of course the error message will be improved once the implementation supports return in the control invocation syntax. A closure written using the => syntax, or converted to a function type written using the => syntax, will be “restricted” and not allowed to return from the enclosing method. See , specifically the last two paragraphs, for a discussion of the way the semantics will work once implemented. I called this to Josh’s attention about two weeks before his presentation, so I don’t know why he included it as if it is an unsolved problem.

  7. jbloch says:

    Sorry, I see no mention of this rule (concerning the => syntax and restricted closures) in the proposal (http://www.javac.info/closures-v05.html), and I have no recollection of the conversation that Neal refers to. When I prepared the “Closures Controversy” talk, I based it on the best and most current information at my disposal. And I continue to believe that adding non-local returns to Java would add enormous and unnecessary complexity. (Of course non-local returns make good sense in languages like Ruby, which were designed around closure-based control structures.)

    Josh

  8. peter says:

    The closures-v05 document is quite scarce on details concerning the RestrictedInterface. The blog post on Neals’ blog is a better read to understand it.

    I do however feel that this sort of behavior should be intuitive and having different types of closures doesn’t really help IMHO. Also, the solution using intersection makes method definitions far to complex! Say we also want to specify the fact that the block passed to the function might thrown an exception; we would get something like this:

    public <U,V,throws E extends Exception> ParallelArray<V>
    	combine(ParallelArray<U> other, {T,U=>V throws E}&RestrictedFunction combiner) throws E{ ... }
    

    Which is just ugly. And contrived example or not… people WILL start doing this!

  9. nmgafter says:

    Actually, I do not expect to support intersection types. Quite simply, when you write a function type using => or a closure using => it is restricted, and you can’t write a “return” inside the closure.

  10. peter says:

    @nmgafter I read the paragraph about the implicit behavior of function written using => and it makes sense. I’m not to sure if having a different token for non restricted would however be a good idea…? Why would you actually want to allow non-local returns, I personally can’t think of a good case?

  11. nmgafter says:

    @peter One example of a use case would be writing an API acts as a typeswitch. Being able to “return” from the enclosing method is just as useful in a typeswitch as it is in any other kind of switch.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>