2

I played lately a bit with Groovy and was surprised to see that it doesn't support non-local returns from inside a closure. I was quite surprised by this, because I always expected this to be the normal case from the time developing with Smalltalk. JDK8 lambdas also don't support non-local returns as I figured out to my despair. Happily, Scala does.

The question Are non-local returns from inside a closure a must for a closure implementation to be "full-fledged"? Or am I just used to it from Smalltalk, but that needn't be the case.

Code illustration

def list = list(1, 2, 3)
def value = list.forEach { each ->
    println(each)
        if(true)
             return each
    return 5
}

println(value)

I expect it to print "11" and not "1235". At least it shouldn't compile if it would print "1235".

z--
  • 2,186
  • 17
  • 33
OlliP
  • 1,545
  • 11
  • 22
  • 3
    What's the question exactly? Some people will agree with you, and some won't. What will you gain? If you consider this to be a blocking point against groovy, then don't use it. – JB Nizet Jul 15 '13 at 21:08
  • 1
    Well, in the end it boils ddown to the question what does a closure need to support to be a closure? Is an expression that allows access to a free variable already a closure? Or does it need to support non-local returns inn order not to be "fake" closures? – OlliP Jul 15 '13 at 21:23

4 Answers4

1

What you want is list.findResult() instead of list.each().

def list = [1, 2, 3]
def value = list.findResult { each ->
  println(each)
  if(each)
    return each
  return 5
}
println(value)
// prints 1<newline>1
jesseplymale
  • 704
  • 5
  • 7
  • So Groovy does support non-local returns from within the findResult method. That is good news. Nevertheless, the code I displayed should have worked as expected or should not have compiled. So I'm not really sure what the downratings were exactly for. Anyway, the question was what makes up for a closure. Are non-local returns or not a substantial matter without which a closure is not a "real" closure or is it more of a nicety? Wonder whether there are any established definitions of what a closure is. What I have found on the Internet is rather vague ... Thanks, Oliver – OlliP Jul 16 '13 at 07:58
  • This Scala code compiles but then also prints "11" as expected: object Foo { def main(args: Array[String]) { println(bar) } def bar : Int = { val list = List(1, 2, 3) list.foreach { each => if(true) { return each } 5 } } – OlliP Jul 16 '13 at 07:59
  • @OliverPlow Groovy's each will continue until all elements have been iterated. So your argument is really with `each` vs `foreach` rather than Closures per-se. – tim_yates Jul 16 '13 at 10:12
  • @TimYares: Thanks, Tim. But I don't understand your point. There seems to be no foreach iterator in Collection and also not as a language keyword. Do you mean a plain for loop? – OlliP Jul 16 '13 at 10:44
  • Another thing I don't understand is why it is possible to return from within findResult, but not from within forEach. So the whole issue is not related to closures ?! I'm a bit confused now ... – OlliP Jul 16 '13 at 11:40
  • Because `findResult` stops when it finds the first result, and `each` iterates over each element in the collection. It's the semantics of the method, not a closure issue – tim_yates Jul 16 '13 at 14:29
  • Groovy also provides a [`Collection.findResults()`](http://groovy.codehaus.org/groovy-jdk/java/util/Collection.html#findResults(groovy.lang.Closure)) (notice the **s** on `findResults()`) which doesn't stop when it finds the first result. It throws away any return values which are null. – jesseplymale Jul 20 '13 at 18:59
1

Found no data to back me up, but here's my 0.02: A closure must return only from itself, and not from a parent caller. The return from a parent caller is the same concept used in ruby's blocks(1): the control/scope of both blocks (callee and caller) is the same, while the closure should be on its own.

The scala way of doing it is a syntatic sugar to do it in groovy: "return" from the closure by throwing an exception.

To "return" from a closure in groovy, stick to find, findAll, or findResult (as per @jesseplymale's answer): these are iterating through the right elements.

...

I'm no guru in javascript, but he seems to agree with me:

// prints every item; the return is only from the function
[1, 2, 3, 4, 5].forEach( function(item) {
  console.log(item);
  return item;
}) 

Ruby, with blocks and lambdas:

# block: prints "1" and stop iterating
def a()
  [1, 2, 3, 4, 5].each { |it|
    puts it
    return it 
  }
end

a()

Passing a block and a lambda as a parameter:

# this method receives a block/lambda and passes 
# it to the 'each' method from list
def b(block)
  [1, 2, 3, 4, 5].each &block
end

# this block fails with "unexpected return (LocalJumpError)"
#b(Proc.new { |it| puts it; return it; }) 

# this lambda keeps iterating and printing, even though 
# it has the return statement
b(lambda { |it| 
  puts it
  return it
})

(1): Note i'm no guru in Ruby, and this might be more or less wrong.

Will
  • 14,348
  • 1
  • 42
  • 44
  • Thanks a lot, Will. I had written the same thing in Scala and was pleased that Scala behaves the same way as I was used to from Smalltalk. Really unpleasant to see that Scala only resorts to throwing an exception to do the non-local return. But that was good information! I wonder whether this is incurred by the JVM ... – OlliP Jul 16 '13 at 13:25
  • I guess so. I think most closures/lambdas/SAMs are implemented using anonymous classes, and not real loops. Maybe if the compiler could make it into a method... – Will Jul 16 '13 at 16:38
  • Wikipedia http://en.wikipedia.org/wiki/Closure_%28computer_science%29 says a few words about local vs non local/return, and it seems that ruby's Proc and lambda behavior differs from this POV – aka.nice Jul 16 '13 at 19:50
  • @WillP wikipedia tells Proc return will be non local, while lambda return will be local, and gives an example. I did not check though... – aka.nice Jul 16 '13 at 20:17
1

I'll throw my two cents in as well. I don't think there is a definitive answer to what makes closures more full fledged than another. But...

I think Smalltalkers are pretty proud (rightfully so) of their closures. I once heard that "Smalltalk blocks did more for the cause of lambda than any other lambda sporting language."

Most language references have a section devoted to "flow control". But Smalltalk is the only language I know of that dared not only to do "objects all the way down" but "closures all the way done." All of the flow control (loops, logic branching, etc) in Smalltalk are done with closures (or at least appear to do so, pay no attention to that optimizing compiler behind the curtain!).

Travis Griggs
  • 21,522
  • 19
  • 91
  • 167
0

Lexical closure with local return is the most usual way of implementing lambdas. Actually the non local return in Smalltalk/Ruby blocks are the exception to most other languages. Some Smalltalks don't even really close blocks over lexical scope, so you may question to what extent blocks can be thought of as closures.

If you'd go to Smalltalk/Ruby from Scheme (as i did), your question might have been the opposite. I.e: "Should Smalltalk blocks have local return to be deemed 'full-fleged closures'?"

I.e: the non local return is there (mostly) to simplify handling control structures, but it's a special mechanism. You could call it an escaping continuation, where flow of execution escapes or jumps to another point.

Non local returns are not an integral part of closures, just a special case, and can be implemented independently (reified continuations, exceptions, other special constructs).

See this (old) article: http://smalltalk.gnu.org/blog/s11001001/lexical-block-return-language-crux

Here's a related discussion about closures and returns: http://lambda-the-ultimate.org/node/2009

[edit]

Found this related question:

Exactly what is the difference between a "closure" and a "block"?

Community
  • 1
  • 1
fede s.
  • 582
  • 3
  • 17