Project

General

Profile

Actions

Bug #11993

closed

foo(hash) is handled like foo(**hash)

Bug #11993: foo(hash) is handled like foo(**hash)

Added by sos4nt (Stefan Schüßler) almost 10 years ago. Updated about 7 years ago.

Status:
Rejected
Assignee:
-
Target version:
-
[ruby-core:72871]

Description

Given this method:

def foo(a = nil, b: nil) p a: a, b: b end 

I can pass keyword arguments to it and I can turn a hash into keyword arguments with the ** operator:

foo(b: 1) #=> {:a=>nil, :b=>1} foo(**{b: 1}) #=> {:a=>nil, :b=>1} 

What baffles me is that a hash is also turned into keyword arguments without the ** operator:

foo({b: 1}) #=> {:a=>nil, :b=>1} 

This looks like a flaw to me. I was expecting:

foo({b: 1}) #=> {:a=>{:b=>1}, :b=>nil} 

Which would have resembled the way * works:

def bar(a = nil, b = nil) p a: a, b: b end bar(1, 2) #=> {:a=>1, :b=>2} bar(*[1, 2]) #=> {:a=>1, :b=>2} bar([1, 2]) #=> {:a=>[1, 2], :b=>nil} 

But currently, there doesn't seem to be a difference between foo(hash) and foo(**hash).

Is this behavior intended? If so, what's the rationale behind this decision?

Updated by avit (Andrew Vit) almost 10 years ago Actions #1 [ruby-core:73616]

See #11967 for Marc-Andre's explanation.

Updated by marcandre (Marc-Andre Lafortune) about 7 years ago Actions #2 [ruby-core:88868]

  • Status changed from Open to Rejected

First, foo(b: 1) has been exactly the same as foo({b: 1}) since Ruby 1.8 at least. It is parsed exactly the same way. It's syntax sugar.

require 'ripper' Ripper.sexp('foo(a : 1)') == Ripper.sexp('foo({a : 1})') # => true 

As you note, the ** operator is not needed in many cases, but there are cases where it matters. You can see a difference in these three cases:

a) It merges keyword arguments:

h = {a: 1, b: 2} p(h, c: 3) # => {:a=>1, :b=>2}, then {:c=>3} p(**h, c: 3) # {:a=>1, :b=>2, :c=>3} 

b) It insures that a hash is viewed as keyword arguments:

h = {'a' => 1} p(h) # {"a"=>1} p(**h) # => TypeError (hash key "a" is not a Symbol) 

c) It differentiates between an actual empty hash {} and nothing at all

def foo(x) p x end e = {} foo('hello', **e) # => 'hello' foo('hello', e) # => ArgumentError (wrong number of arguments (given 2, expected 1)) 

Note that some corner cases may not perfectly handled yet (#15078)

In summary: using ** improves legibility by making the intention crystal clear, makes your code stricter and allows you to easily merge options. There is also discussion to make the use of ** required in some cases in Ruby 3.0 (see #14183).

I'm closing this, but will reopen if need be.

Actions

Also available in: PDF Atom