Witness:
$a = 0
def foo(a)
$a += 1
retry if $a < 4
end
(puts 'here'; self).foo((puts 'there'; nil)) {}
Output:
~/NetBeansProjects/jruby $ ruby test.rb
here
there
here
there
here
there
here
there
What's happening here is that a retry within a method body (which works
*only* if the method has been passed a block) causes both the receiver
and the arguments to the original call to be re-evaluated.
Sure, it's useful for this:
def my_while(cond)
return unless cond
yield
retry
end
my_while(a < 4) { do something }
But there's a serious danger here. I could define APIs that cause the
caller's code to do things the caller did not intend:
# two operations that I *must* invoke only once
def open_singleton; end
def something_else; end
create_singleton.foo(something_else) { some body }
If foo is implemented with a retry as above, it will cause
create_singleton and something_else to get called several times. Even
worse, there's no way to know by looking at it and no way to avoid it.
The call to the method has undetectable, unavoidable, potentially
dangerous side effects on the caller.
I do not believe a target method should be able to cause the caller
method to behave this way. I believe this behavior should be
deprecated/removed.
- Charlie