Instance_eval block argument - documented? target?

I just realized that instance_eval gives self as an argument to the associated block (with the exception of an error in version 1.9.2: http://www.ruby-forum.com/topic/189422 )

 1.9.3p194 :003 > class C;end 1.9.3p194 :004 > C.new.instance_eval {|*a| a} => [#<C:0x00000001f99dd0>] 1.9.3p194 :005 > 

Is this documented / spec 'ed somewhere? Looking at ruby-doc: BasicObject , no block parameters are visible.

Is there a reason - a pair from some purely historical one - to express it explicitly when it is always always determined?


How it struck me:

 l = lambda { } myobj.instance_eval(&l) # barks 

This worked fine in 1.8.x (I think it was not done due to blocking).

Then upgraded to 1.9.2 - and it still works! This is a strange coincidence, because although the arguments of the lambda block are strictly respected (therefore, he would complain that he did not declare the argument for himself), but because of the error related above, the fact itself was not conveyed in this version ..

It then upgraded to 1.9.3, where this error was fixed, so it began to throw an argument error - quite surprisingly for a minor version of IMHO.

So, one solution is to declare a parameter or make a lambda block instead:

  l = proc { } myobj.instance_eval(&l) # fine 

Just thought to describe the whole story, to help others avoid wasting time like I did until it is properly documented.

+7
source share
3 answers

Reading the Ruby source code, I can interpret:

instance_eval does the following:

 return specific_eval(argc, argv, klass, self) 

which in turn starts:

  if (rb_block_given_p()) { if (argc > 0) { rb_raise(rb_eArgError, "wrong number of arguments (%d for 0)", argc); } return yield_under(klass, self, Qundef); } 

You can see that they pass Qundef for the VALUES argument.

 if (values == Qundef) { return vm_yield_with_cref(th, 1, &self, cref); } 

In this particular line of code, they manually set argc (argument count) to 1, and the argument to self. Later, the code that prepares the block sets the arguments to the block for these arguments, so the first argument is "self", and the rest is nil.

The code that sets the block arguments executes:

  arg0 = argv[0]; ... bunch of code ... else { argv[0] = arg0; } for (i=argc; i<m; i++) { argv[i] = Qnil; } 

Result:

 1.9.3p194 :006 > instance_eval do |x, y, z, a, b, c, d| x.class end => Object 1.9.3p194 :008 > instance_eval do |x, y, z, a, b, c, d| y.class end => NilClass 

Why? I have no idea, but the code seems intentional. It would be nice to ask the performers a question and see what they can say about it.

[change]

This is probably the case because the blocks you pass to instance_eval may or may not be created for it (code that depends on whether it is set up for the class that the block needs to be changed), instead they can assume that you are going to pass them the instance that you want to change as an argument, and that way they will work with instance_eval.

 irb(main):001:0> blk = Proc.new do |x| x.class end #<Proc: 0x007fd2018447b8@ (irb):1> irb(main):002:0> blk.call NilClass irb(main):003:0> instance_eval &blk Object 

Of course, this is only a theory and without official documentation, which I can only guess about.

+3
source

I just confirmed that, unlike #instance_eval, which is primarily intended to evaluate strings, #instance_exec, which is primarily intended to evaluate a block, does not have the described behavior:

 o = Object.new o.instance_exec { |*a| puts "a.size is #{a.size}" } => a.size is 0 

This is probably an unintentional inconsistency, so you might find an error. Publish it on Ruby bugs .

+1
source

I just asked the same question: Ruby lambda proc and 'instance_eval'

And after reading the answer and working through some code, I think I understand why ruby ​​has this strange (IMHO) inconsistency.

This basically allows Symbol#to_proc to work.

For example, ["foo", "bar"].each(&:puts) is short for [...].each { |x| puts x } [...].each { |x| puts x } [...].each { |x| puts x } [...].each { |x| puts x }

NOT

[...].each { self.puts }

Thus, ruby ​​also passes self as the first parameter to proc, so basically proc can use self or its first parameter.

Since an eval instance does not, by definition, pass parameters explicitly, this is almost always an invisible behavior.

The exception is when the process is a lambda expression. This does not work:

 2.4.1 :015 > foo = -> { puts 'hi' } => #<Proc: 0x007fcb578ece78@ (irb):15 (lambda)> 2.4.1 :016 > [1, 2, 3].each(&foo) ArgumentError: wrong number of arguments (given 1, expected 0) from (irb):15:in 'block in irb_binding' from (irb):16:in 'each' from (irb):16 

Therefore, I think that the only time this becomes a problem is when instance_eval is used with some unknown value, when you do not know if the procedure is lambda or not. In this case, you should do it like this:

proc_var.lambda?? instance_exec(&proc_var): instance_eval(&proc_var)

It’s strange (for me) that a ruby ​​just doesn’t do this under the hood for you.

but I think you could do it like this:

 alias original_instance_eval instance_eval def instance_eval(*args, &block) block&.lambda? ? instance_exec(&block) : original_instance_eval(*args, &block) end 
0
source

All Articles