4

Why does this snippet:

def dump_dump
    get_dump = lambda do
        return 1 if $n
        $n = true
        module_exec &get_dump
        2
    end
    p get_dump[]
end

Module.new do
    module_exec &method(:dump_dump)
end

print 2 in ruby 2.0.0p481 (2014-05-08) [x64-mingw32]
but 1 in jruby 1.7.15 (1.9.3p392) 2014-09-03 82b5cc3 on Java HotSpot(TM) 64-Bit Server VM 1.7.0_67-b01 +jit [Windows 8-amd64]?

I would like to understand the issue.

UPD: should it be reported somewhere?

Nakilon
  • 34,866
  • 14
  • 107
  • 142

2 Answers2

2

I was always under the impression that return inside a block was undefined behavior. Can you use next instead?

For instance, Rubinius also has this problem but is much more explicit:

[1].map(&lambda { |n| return -1 })
LocalJumpError: unexpected return

Of course, using next produces the expected results:

rbx-head :003 > [1].map(&lambda { |n| next -1 })
 => [-1] 

The moral of the story is that return is defined for methods, and Procs and lambdas are not methods. next and break are the keywords to use if you wish to stop a block call.

I can't find any documentation about return's behavior from the official Ruby spec, but rubyspec does have tests that verify return causes the calling method to return.

https://github.com/rubyspec/rubyspec/blob/master/language/return_spec.rb#L184

Max
  • 15,157
  • 17
  • 82
  • 127
  • Blocks shouldn't handle return, only the surrounding method. However lambda should have methods semantics. When converting to a block a lambda retains its lambda status so.... This is all I found for reference https://gist.github.com/mislav/4508988 – adzdavies Sep 19 '14 at 21:43
  • I just tested this. In MRI, the lambda keeps its lambda properties when calling `to_proc`. In rbx, on the other hand, the lambda properties are lost (e.g. missing methods do not throw an error). – Max Sep 22 '14 at 04:14
0

A 'return' inside a lambda should return from the lambda and not return from the method. In this tricky case, it looks like jruby is not respecting the inner lambda, and instead returning all the way back to the first lambda call.

At first I thought it might be caused by a lambda call within a lambda, but now I believe it to be an issue related to block-conversion, after reducing the example down to this:

Module.new do
  test = lambda do
    return
  end
  module_exec &test
  puts 'after'
end

Here only mri prints 'after', while jruby prints nothing.

...but if we do NOT do lambda to block conversion (the &test):

Module.new do
  test = lambda do
    return
  end
  module_exec { test[] }
  puts 'after'
end

both mri and jruby print 'after'...

adzdavies
  • 1,545
  • 1
  • 13
  • 13
  • Are you sure you didn't replace them with each other? My jruby returns through all the calls -- that's how I spotted the issue. The original code was traversing a tree and had to return a long vector, but jruby was throwing me nil, raising exception further in my program. – Nakilon Sep 19 '14 at 09:49
  • As I remember, when I tried in my very original code to use `{ test[] }` instead of `&test`, it had another scope and didn't see module methods, etc., so this replacing isn't equivalent. – Nakilon Sep 19 '14 at 12:53
  • true -- I was trying to locate the cause. Try this: def test; [1].map(&lambda{ |n| return -1 }); end – adzdavies Sep 19 '14 at 13:00
  • I've just added this to jruby issue tracker: https://github.com/jruby/jruby/issues/1985 – adzdavies Sep 19 '14 at 13:07