Bug #11283
closedBlock assigned implicitly
Description
That is how it works:
module Test
def self.build(&block)
klass = Class.new(Object)
klass.__send__(:define_method, :foo)
klass.__send__(:define_method, :bar)
klass
end
end
Tested = Test.build { :foo }
# warning: tried to create Proc object without a block
# => Tested
Tested.new.foo
# => :foo
Tested.new.bar
# => :foo
The block is assigned to all calls to :define_method
via Object#__send__
implicitly, while it wasn't asked to.
The behaviour is tested under MRI 1.9.3, 2.0, 2.1, 2.2, ruby-head. It doesn't occur under rbx-2 and jruby (1.7, 9.0.0.0).
For the context look at this thread https://github.com/mbj/mutant/issues/356
Updated by nepalez (Andrew Kozin) over 9 years ago
- Description updated (diff)
Updated by nepalez (Andrew Kozin) over 9 years ago
- Description updated (diff)
Updated by nepalez (Andrew Kozin) over 9 years ago
- Description updated (diff)
Updated by nepalez (Andrew Kozin) over 9 years ago
- Description updated (diff)
Updated by nepalez (Andrew Kozin) over 9 years ago
- Description updated (diff)
Updated by nepalez (Andrew Kozin) over 9 years ago
- Description updated (diff)
Updated by funny_falcon (Yura Sokolov) over 9 years ago
This is "hidden feature", not the bug: Proc.new
can consume unnamed block passed to method.
It makes possible following code:
def do_now_or_later
if can_do_now?
yield
else
do_it_later Proc.new
end
end
do_now_or_later { puts 'hi' }
In your case block is passed to implicit Proc.new
in the implementation of define_method
.
I agree, that it is a bit weird feature.
Updated by nepalez (Andrew Kozin) over 9 years ago
This feature buzzes and wiggles aerials like a bug.
I cannot see why is it needed for your example. It is pretty explicit and has no hidden calls. Both the yield
and Proc.new
are used intentionally, while in my examples they weren't.
Updated by 0x0dea (D.E. Akers) over 9 years ago
Andrew, what would you like Ruby to do when you attempt to define a method with no body?
Updated by nepalez (Andrew Kozin) over 9 years ago
I'd expect Ruby to do any of two options:
- Either provide a method with empty proc (
Proc.new
) - Or call
SyntaxError
as a strict way to ask for the programmer's intention
I think any of these options could follow POLA better than the current one
Updated by 0x0dea (D.E. Akers) over 9 years ago
- Either provide a method with empty proc (
Proc.new
)
That is, in fact, exactly what Ruby is doing:
def build
Class.new { define_method :foo, Proc.new }
end
build{ :bar }.new.foo # => :bar
Proc.new
, called without an explicit block, will instead attempt to use the one that was passed to the surrounding method:
def foo
Proc.new.call 1
end
foo { |x| x + 1 } # => 2
Note well that methods need not explicitly declare that they take a block. This double whammy of implicit behavior is certainly "astonishing" the first time you encounter it, but it all hangs together in the final analysis.
- Or call
SyntaxError
as a strict way to ask for the programmer's intention
This is a semantic rather than syntactic concern, and Ruby does warn against it. That it doesn't raise an ArgumentError
as it does for standalone Proc.new
is indeed unexpected, and may well be a bug.
Updated by nepalez (Andrew Kozin) over 9 years ago
Not, in my examples (from the initial post) it does not do that, but something different. Namely, it doesn't assigning a new proc (that would be OK)
Instead, the interpreter looks around in search what else it could use when I doesn't send a proc, then takes what it found somewhere and pushes it to the method.
MRI doesn't let me decide whether the method should be called without a proc (that should be interpreted as asking for Proc.new
, as in your example).
This is the bug, not the way it works out the :define_method
call.
Updated by nobu (Nobuyoshi Nakada) over 9 years ago
- Status changed from Open to Closed
Applied in changeset r50971.
proc.c: ArgumentError if no block
- proc.c (rb_mod_define_method): now requires a block direct to
this method call. [ruby-core:69655] [Bug #11283]
Updated by usa (Usaku NAKAMURA) over 9 years ago
- Backport changed from 2.0.0: UNKNOWN, 2.1: UNKNOWN, 2.2: UNKNOWN to 2.0.0: WONTFIX, 2.1: REQUIRED, 2.2: REQUIRED
Updated by nagachika (Tomoyuki Chikanaga) over 9 years ago
- Backport changed from 2.0.0: WONTFIX, 2.1: REQUIRED, 2.2: REQUIRED to 2.0.0: WONTFIX, 2.1: REQUIRED, 2.2: WONTFIX
The change of define_method's behavior is added into NEWS file at r50971. I think this is a spec change even thought original behavior is confusing.
Updated by usa (Usaku NAKAMURA) over 9 years ago
- Backport changed from 2.0.0: WONTFIX, 2.1: REQUIRED, 2.2: WONTFIX to 2.0.0: WONTFIX, 2.1: WONTFIX, 2.2: WONTFIX
I agree with you, chikanaga-san.