Bug #6151
closedArgumentError of lambda shows wrong location
Description
=begin
Perhaps there is a reason, but it sure seems like a bug to me. Given this simple class:
#
class BlockParser < BasicObject
def initialize(data={}, &block)
@data = data
instance_eval(&block)
end
def method_missing(name, *args, &block)
if block
@data[name] = {}
BlockParser.new(@data[name], &block)
else
case args.size
when 0
@data[name]
when 1
@data[name] = args.first
else
@data[name] = args
end
end
end
end
Then we can do:
data = {}
BlockParser.new(data) do
name 'Tommy'
end
data #=> {:name=>'Tommy'}
But,
data = {}
blk = lambda do
name 'Tommy'
end
BlockParser.new(data, &blk)
ArgumentError: wrong number of arguments (1 for 0)
from (irb):44:in `block in irb_binding'
from (irb):16:in `instance_eval'
from (irb):16:in `initialize'
from (irb):46:in `new'
from (irb):46
from /home/trans/.rbfu/rubies/1.9.3-p0/bin/irb:12:in `<main>'
If I use (({Proc.new})) instead of (({lambda})) it works. But since I am using (({#instance_eval})) to evaluate the Proc, that shouldn't matter.
Note the reported line number of the error corresponds to (({name 'Tommy'})).
=end
Updated by trans (Thomas Sawyer) almost 13 years ago
That's fun, go to report a bug in Ruby and hit a bug in Ruby's Bug tracker.
Try again, this time with no RD crap.
Given:
#
class BlockParser < BasicObject
def initialize(data={}, &block)
@data = data
instance_eval(&block)
end
def method_missing(name, *args, &block)
if block
@data[name] = {}
BlockParser.new(@data[name], &block)
else
case args.size
when 0
@data[name]
when 1
@data[name] = args.first
else
@data[name] = args
end
end
end
end
data = {}
blk = lambda do
name 'tommy'
end
BlockParser.new(data, &blk)
ArgumentError: wrong number of arguments (1 for 0)
But it works if we use:
blk = Proc.new do
name 'tommy'
end
Since BlockParser is using instance_eval, I don't see why this should be an error. Indeed, how can I depend on it if my API simply accepts a block --I don't know if it's a Proc or a lambda.
Updated by nobu (Nobuyoshi Nakada) almost 13 years ago
- Description updated (diff)
Updated by nobu (Nobuyoshi Nakada) almost 13 years ago
- Subject changed from BasicObject instance_eval of lambda causes errors to ArgumentError of lambda shows wrong location
- Target version set to 2.0.0
You need to indent with spaces, before tabs.
Updated by nobu (Nobuyoshi Nakada) almost 13 years ago
- Status changed from Open to Closed
- % Done changed from 0 to 100
fixed in r35051-r35053.
Updated by john_firebaugh (John Firebaugh) almost 13 years ago
The reason it raises an error is that instance_eval yields one argument (the receiver), and lambdas are strict about arity -- see #2476.
Updated by trans (Thomas Sawyer) almost 13 years ago
Just to be clear, where both issues fixed? i.e. the fact that an error was raised in this case at all, as well as the location of lambda errors?
Updated by john_firebaugh (John Firebaugh) almost 13 years ago
Just the location was fixed. The error is "by design" -- as you discovered, you will need to use Proc.new (or lambda {|*| ... }).
Updated by trans (Thomas Sawyer) almost 13 years ago
How can that be by design? What kind of design criteria says that "instance_eval should blow up if a lambda is passed to it"?
So you are saying that instance_eval passes the receiver into the Proc as an argument? Why would it do this when the Block has zero arity? Why not just check the arity of the block and pass it if it can take an argument otherwise not?
This snafu is really ashame for me too, b/c I finally had a good reason to use Ruby 1.9's ->
operator!
Updated by TylerRick (Tyler Rick) almost 12 years ago
I agree, @trans (Thomas Sawyer), this is a very surprising behavior. I was expecting instance_eval to call the block I passed to it without any args. Since self is already implicitly available from the block, I just don't understand why instance_eval would yield self as an argument.
Fortunately, I think instance_exec http://ruby-doc.org/core-2.0/BasicObject.html#method-i-instance_exec does what we are wanting so I'm using it instead.
We should update the documentation. http://ruby-doc.org/core-2.0/BasicObject.html#method-i-instance_eval does not mention that it yields self as the first argument.