Bug #5328

Splat args are treated wrongly in instance_exec and class_exec

Added by Myron Marston over 3 years ago. Updated 12 months ago.

[ruby-core:39562]
Status:Rejected
Priority:Normal
Assignee:Akira Tanaka
ruby -v:ruby 1.9.2p290 (2011-07-09 revision 32553) [x86_64-darwin10.8.0] Backport:

Description

This gist demonstrates the issue:

https://gist.github.com/1218664

On 1.8.7, instance_exec and class_exec treat splat block args the same as method splat args. On 1.9, it is treated differently and this is very unexpected.

History

#1 Updated by Koichi Sasada about 3 years ago

  • Status changed from Open to Assigned
  • Assignee set to Akira Tanaka

#2 Updated by Akira Tanaka 12 months ago

  • Status changed from Assigned to Rejected

Ruby 1.9 changes block arguments behavior.
Non-lambda block arguments always expands single array.
(Ruby 1.8 expands sometimes.)

If you needs block arguments behaving as methods, use lambda.

% cat splat_args
def foo(arg_1, *arg_2)
  puts "Arguments received by method:"
  puts "arg_1: #{arg_1.inspect}"
  puts "arg_2: #{arg_2.inspect}"
end
foo([1, 2, 3])

puts

Class.class_exec([1, 2, 3]) do |arg_1, *arg_2|
  puts "Arguments received by class_exec:"
  puts "arg_1: #{arg_1.inspect}"
  puts "arg_2: #{arg_2.inspect}"
end

puts

instance_exec([1, 2, 3]) do |arg_1, *arg_2|
  puts "Arguments received by instance_exec:"
  puts "arg_1: #{arg_1.inspect}"
  puts "arg_2: #{arg_2.inspect}"
end

puts

Class.class_exec([1, 2, 3], &lambda do |arg_1, *arg_2|
  puts "Arguments received by class_exec(lambda):"
  puts "arg_1: #{arg_1.inspect}"
  puts "arg_2: #{arg_2.inspect}"
end)

puts

instance_exec([1, 2, 3], &lambda do |arg_1, *arg_2|
  puts "Arguments received by instance_exec(lambda):"
  puts "arg_1: #{arg_1.inspect}"
  puts "arg_2: #{arg_2.inspect}"
end)

% ruby-1.9.1p0 splat_args
Arguments received by method:
arg_1: [1, 2, 3]
arg_2: []

Arguments received by class_exec:
arg_1: 1
arg_2: [2, 3]

Arguments received by instance_exec:
arg_1: 1
arg_2: [2, 3]

Arguments received by class_exec(lambda):
arg_1: [1, 2, 3]
arg_2: []

Arguments received by instance_exec(lambda):
arg_1: [1, 2, 3]
arg_2: []

#3 Updated by Myron Marston 12 months ago

Thanks for taking the time to explain.

This surprises me a bit because I've followed ruby development closely for a number of years and this is the first time I've heard of this particular proc vs lambda difference (I know of others like the difference in return and ArgumentError if a lambda is given the wrong number of args). It also seems very counterintuitive to me.

Is there a prior issue where this was discussed? I'd like to read up on it.

Thanks!

#4 Updated by Akira Tanaka 12 months ago

I can remember several documents.

  • The document of Proc#lambda?.
  • The thread from ruby-core:47453 "yield plus splat unwraps too much" (I don't recommend reading it in bugs.ruby-lang.org because messages are unintentionally formatted as markdown.)
  • ruby-dev:38795 (in Japanese)

Note that "always expands single array" was mistake.
The expantion also depends on the form of block arguments.

#5 Updated by Myron Marston 12 months ago

Thanks. I'm still trying to wrap my head around it but that's quite helpful.

Also available in: Atom PDF