Instance eval in 1.8 and 1.9

Hi,

Porting a program from 1.8 to 1.9, I hit a stumbling block in the use
of instance_eval.

The following code works in 1.8 but not 1.9. instance_eval in 1.9
does not allow you to use a lambda as a block. The solution is to use
a proc instead. I understand that lambdas are like methods and procs
are like blocks, but what gets me is the incredibly unhelpful error
message.

Anyway, the code (can also see it at http://gist.github.com/479572 )

  class InstanceEval
    def initialize(x)
      @block = x
      @context = Object.new
    end

    def run
      code = @block
      result = @object.instance_eval(&code)
      "* run --> #{result.inspect}"
    end
  end

  p = proc { :stuck_out_tongue: }
  l = lambda { :l } # (line 16 -- for the error message below)

  puts InstanceEval.new(p).run
  puts InstanceEval.new(l).run

The output in 1.8:

  * run --> :stuck_out_tongue:
  * run --> :l

The output in 1.9:

  * run --> :stuck_out_tongue:
  code.rb:16:in `block in <main>': wrong number of arguments (1 for 0)
(ArgumentError)
          from code.rb:10:in `instance_eval'
          from code.rb:10:in `run'
          from code.rb:19:in `<main>'

To what method have I passed one argument where zero arguments were expected?

Is the average Ruby programmer supposed to know that instance_eval
can't take a lambda in Ruby 1.9? Perhaps it should say so in the
documentation. (http://bit.ly/96HBeb)

Is there a good reason ( instance_eval &lambda_object ) can't work?

Thanks for any light you can shed on it.

Gavin

Your code runs without errors for me with ruby 1.9.1-p376.

Stefano

···

On Saturday 17 July 2010, Gavin Sinclair wrote:

>Hi,
>
>Porting a program from 1.8 to 1.9, I hit a stumbling block in the use
>of instance_eval.
>
>The following code works in 1.8 but not 1.9. instance_eval in 1.9
>does not allow you to use a lambda as a block. The solution is to use
>a proc instead. I understand that lambdas are like methods and procs
>are like blocks, but what gets me is the incredibly unhelpful error
>message.
>
>Anyway, the code (can also see it at Ruby 1.9.2-rc1: instance_eval can't handle a lambda (needs a proc) · GitHub )
>
> class InstanceEval
> def initialize(x)
> @block = x
> @context = Object.new
> end
>
> def run
> code = @block
> result = @object.instance_eval(&code)
> "* run --> #{result.inspect}"
> end
> end
>
> p = proc { :stuck_out_tongue: }
> l = lambda { :l } # (line 16 -- for the error message
>below)
>
> puts InstanceEval.new(p).run
> puts InstanceEval.new(l).run
>
>
>The output in 1.8:
>
> * run --> :stuck_out_tongue:
> * run --> :l
>
>The output in 1.9:
>
> * run --> :stuck_out_tongue:
> code.rb:16:in `block in <main>': wrong number of arguments (1 for 0)
>(ArgumentError)
> from code.rb:10:in `instance_eval'
> from code.rb:10:in `run'
> from code.rb:19:in `<main>'
>
>To what method have I passed one argument where zero arguments were
>expected?
>
>Is the average Ruby programmer supposed to know that instance_eval
>can't take a lambda in Ruby 1.9? Perhaps it should say so in the
>documentation. (http://bit.ly/96HBeb\)
>
>Is there a good reason ( instance_eval &lambda_object ) can't work?
>
>Thanks for any light you can shed on it.
>
>Gavin

Your code runs without errors for me with ruby 1.9.1-p376.

Stefano

Thanks for testing it. I should have specified my version, of course:

  ruby 1.9.2dev (2010-07-02 revision 28524) [i386-cygwin]

Gavin

It seems like this could be a regression. The error message at the
very least is a bug. I suggest you file a bug report for this.

···

On 7/17/10, Gavin Sinclair <gsinclair@gmail.com> wrote:

Your code runs without errors for me with ruby 1.9.1-p376.

Stefano

Thanks for testing it. I should have specified my version, of course:

  ruby 1.9.2dev (2010-07-02 revision 28524) [i386-cygwin]

Caleb Clausen wrote:

···

On 7/17/10, Gavin Sinclair <gsinclair@gmail.com> wrote:

Your code runs without errors for me with ruby 1.9.1-p376.

Stefano

Thanks for testing it. I should have specified my version, of course:

  ruby 1.9.2dev (2010-07-02 revision 28524) [i386-cygwin]

It seems like this could be a regression.

Seconded. The program works OK for me with ruby 1.9.2dev (2009-07-18
trunk 24186) [i686-linux]
--
Posted via http://www.ruby-forum.com/\.

Nobu says this is not a bug, and his explanation makes perfect sense
to me, now that I understand it. instance_eval passes self as a
parameter to the block in 1.8 and 1.9.2 but didn't in 1.9.1. Also,
lambda panics if it doesn't receive the expected number of params,
whereas proc does not.

See ruby-core:31336

···

On 7/19/10, Brian Candler <b.candler@pobox.com> wrote:

Caleb Clausen wrote:

On 7/17/10, Gavin Sinclair <gsinclair@gmail.com> wrote:

Your code runs without errors for me with ruby 1.9.1-p376.

Stefano

Thanks for testing it. I should have specified my version, of course:

  ruby 1.9.2dev (2010-07-02 revision 28524) [i386-cygwin]

It seems like this could be a regression.

Seconded. The program works OK for me with ruby 1.9.2dev (2009-07-18
trunk 24186) [i686-linux]

It seems like this could be a regression.

Seconded. The program works OK for me with ruby 1.9.2dev (2009-07-18
trunk 24186) [i686-linux]

Data so far:
* works on 1.9.1-p376 [Caleb, Gavin]
* works on 1.9.2dev (2009-07-18) [Brian]
* doesn't work on 1.9.2dev (2010-07-02) [Gavin]

That agrees with Nobu's comment on ruby-core (which I should have
relayed here earlier, sorry):

    No, it not the point. Now instance_eval yields the self.

This code (to boil the problematic case down) fails...

    code = lambda { 42 }
    instance_eval &code

...because instance_eval passes 'self' as a parameter to the block.
Lambdas are fussy about parameters being accounted for, hence the
ArgumentError. Procs are not fussy about parameters, so replace
'lambda' with 'proc' in the above code and it works.

For that matter, _this_ works too:

    code = lambda { |s| 42 }
    instance_eval &code

From what I gather:
* in 1.8.x, instance_eval passed self but it was an undocumented feature
   and lambdas weren't fussy so it didn't matter
* in 1.9.1 instance_eval stopped passing self (don't know whether that change
   was intentional)
* heading towards 1.9.2 it started passing self again, from about December 2009

For a (still?)-undocumented feature, the message "ArgumentError: 0 for
1" is not very helpful.

Gavin