Search code examples
rubylambdaclosuresproc-object

Inconsistency of arity between Hash.each and lambdas


I lifted the following example from Josh Susser

  def strip_accents params
    thunk = lambda do |key,value|
      case value
        when String then value.remove_accents!
        when Hash   then value.each(&thunk)
      end
    end
    params.each(&thunk)
  end

when I put it in the the rails console (irb), and call it with a hash, I get the following:

ruby-1.9.2-p136 :044 > `ruby --version`
 => "ruby 1.9.2p136 (2010-12-25 revision 30365) [i686-linux]\n"
ruby-1.9.2-p136 :045 > strip_accents({:packs=>{:qty=>1}})
ArgumentError: wrong number of arguments (1 for 2)
        from (irb):32:in `block in strip_accents'
        from (irb):37:in `each'
        from (irb):37:in `strip_accents'
        from (irb):45
        from /longpathtrimedforclarity/console.rb:44:in `start'
        from /longpathtrimedforclarity/console.rb:8:in `start'
        from /longpathtrimedforclarity/commands.rb:23:in `<top (required)>'
        from script/rails:6:in `require'
        from script/rails:6:in `<main>'

I understand that lambdas check arity, but I see two arguments in the lambda definition. If I change lambda do to Proc.new do, The code executes, and I get the expected result.

Josh's example is from 2008, so I'm assuming this is a difference in Ruby 1.8 and 1.9. What's going on here?


Solution

  • Indeed, it appears to have changed between 1.8 and 1.9, but this change fixes it for 1.9.2, at least in my tests:

    def strip_accents params
      thunk = lambda do |h|
        key, value = h
        case value
        when String then value.remove_accents!
        when Hash   then value.each(&thunk)
        end
      end
      params.each(&thunk)
    end
    

    This approach turns out to be backward-compatible with Ruby 1.8.7, as well.