Search code examples
rubylambdablockprocsplat

Why do Ruby procs/blocks with splat arguments behave differently than methods and lambdas?


Why do Ruby (2.0) procs/blocks with splat arguments behave differently than methods and lambdas?

def foo (ids, *args)
  p ids
end
foo([1,2,3]) # => [1, 2, 3]

bar = lambda do |ids, *args|
  p ids
end
bar.call([1,2,3]) # => [1, 2, 3]

baz = proc do |ids, *args|
  p ids
end
baz.call([1,2,3]) # => 1

def qux (ids, *args)
  yield ids, *args
end
qux([1,2,3]) { |ids, *args| p ids } # => 1

Here's a confirmation of this behavior, but without explanation: http://makandracards.com/makandra/20641-careful-when-calling-a-ruby-block-with-an-array


Solution

  • There are two types of Proc objects: lambda which handles argument list in the same way as a normal method, and proc which use "tricks" (Proc#lambda?). proc will splat an array if it's the only argument, ignore extra arguments, assign nil to missing ones. You can partially mimic proc behavior with lambda using destructuring:

    ->((x, y)) { [x, y] }[1]         #=> [1, nil]
    ->((x, y)) { [x, y] }[[1, 2]]    #=> [1, 2]
    ->((x, y)) { [x, y] }[[1, 2, 3]] #=> [1, 2]
    ->((x, y)) { [x, y] }[1, 2]      #=> ArgumentError