Project

General

Profile

Actions

Bug #11283

closed

Block assigned implicitly

Added by nepalez (Andrew Kozin) over 9 years ago. Updated over 9 years ago.

Status:
Closed
Assignee:
-
Target version:
-
ruby -v:
1.9.3, 2.0, 2.1, 2.2, ruby-head
[ruby-core:69655]

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.

Actions #13

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

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
Actions #15

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.

Actions #16

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.

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0