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:
  1. static <E> Boolean contains(Iterable<E> seq, {E => Boolean} p) {
  2.     for (E e : seq)
  3.         if (p.invoke(e))
  4.             return true;
  5.     return false;
  6. }
  7.  
  8. static Boolean test() {
  9.     List<Character> list = Arrays.asList(
  10.             'h', 'e', 'l', 'l', 'o');
  11.     return contains(list, { Character c => return c> 'j'; } );
  12. }

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:
  1. def contains(enumerable, &test)
  2.   enumerable.each do |e|
  3.     return true if test.call(e)
  4.   end
  5.   false
  6. end
  7.  
  8. puts contains(['h','e','l','l','o']){|v| return v == "z"}

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:
  1. def contains(enumerable, &test)
  2.   begin
  3.     puts "a"
  4.     enumerable.each do |e|
  5.       return true if test.call(e)
  6.     end
  7.   rescue
  8.     puts "Error in passed test-block: #{$!}"
  9.   ensure   
  10.     puts "b"
  11.   end 
  12.    
  13.   false
  14. end
  15.  
  16. puts contains(['h','e','l','l','o']){|v| return v == "z"}

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.


13 Responses to “Closures and the return of the return”

  1. 1 nmgafter

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

  2. 2 jbloch

    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. 3 jbloch

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

  4. 4 peter

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

  5. 5 peter

    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. 6 nmgafter

    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. 7 nmgafter
  8. 8 jbloch

    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

  9. 9 peter

    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!

  10. 10 nmgafter

    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.

  11. 11 peter

    @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?

  12. 12 nmgafter

    @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.

  13. 13 nmgafter

Leave a Reply





About

Welcome to the weblog of Peter Maas. Here you'll find various posts related to stuff I like (like my kids and espresso) and stuff I do (like developing software).

JavaOne 2008 Pictures


pub Okke en Rudie Cable Car line Moscone Center - JavaOne alcatraz Acme Anvile at CommunityOne Keynote Java + You on a cab Stage being build in the nearby park golden_gate_warning_sign Community One Keynote Charles Nutter & Guillaume Laforge Tim Bray introducing the (J)Ruby panel Stretched Limo sea_lion Hotel room smashmouth Rudie javaone2008 keynote Scribbled Sun Logo Greenland
View more photos >

Categories



Meld u aan voor PayPal en begin direct met het accepteren van creditcardbetalingen.