Bug #8316
closedCan't pass hash to first positional argument; hash interpreted as keyword arguments
Description
I'm able to pass any other type of object to my first argument:
def foo(hash, opt: true)
puts "hash: #{hash}, opt: #{opt.inspect}"
end
foo 'a' # => hash: a, opt: true
foo [{a:1}] # => hash: [{:a=>1}], opt: true
foo [{a:1}], opt: false # => hash: [{:a=>1}], opt: false
But when I try to pass a hash, it raises an ArgumentError:
foo({a:1}) # Raises ArgumentError: unknown keyword: a
Expected behavior: hash: {:a=>1}, opt: true¶
I tried to work around the "unknown keyword" error by using ** but ended up getting a "wrong number of arguments (0 for 1)" error instead.
def foo_with_extra(hash, **extra)
puts "hash: #{hash}, extra: #{extra.inspect}"
end
foo_with_extra 'a' # hash: a, extra: {}
foo_with_extra [{a:1}] # hash: [{:a=>1}], extra: {}
foo_with_extra [{a:1}], opt: false # hash: [{:a=>1}], extra: {:opt=>false}
foo_with_extra({a:1}) # Raises ArgumentError: wrong number of arguments (0 for 1)
Expected behavior: hash: {:a=>1}, extra: {}¶
This behavior is surprising and I haven't seen it mentioned anywhere before. Is it really intentional?
Updated by TylerRick (Tyler Rick) over 11 years ago
=begin
http://jp.rubyist.net/magazine/?Ruby200SpecialEn-kwarg#f01 said:
Be careful when passing hashes to methods with both variable length argument lists and keyword arguments.
but in this example, the argument list is ((not)) variable length.
=end
Updated by phluid61 (Matthew Kerwin) over 11 years ago
TylerRick (Tyler Rick) wrote:
I'm able to pass any other type of object to my first argument:
def foo(hash, opt: true)
puts "hash: #{hash}, opt: #{opt.inspect}"
endfoo 'a' # => hash: a, opt: true
foo [{a:1}] # => hash: [{:a=>1}], opt: true
foo [{a:1}], opt: false # => hash: [{:a=>1}], opt: falseBut when I try to pass a hash, it raises an ArgumentError:
foo({a:1}) # Raises ArgumentError: unknown keyword: a
Expected behavior: hash: {:a=>1}, opt: true¶
Additionally, calling foo({opt:1}) throws ArgumentError: wrong number of arguments (0 for 1)
This issue can be worked around by calling: foo({a:1},{}) , but it is unexpected.
Updated by nobu (Nobuyoshi Nakada) over 11 years ago
- Category set to core
- Status changed from Open to Assigned
- Assignee set to mame (Yusuke Endoh)
Updated by mame (Yusuke Endoh) about 11 years ago
- Status changed from Assigned to Closed
This issue was solved with changeset r40992.
Pablo, thank you for reporting this issue.
Your contribution to Ruby is greatly appreciated.
May Ruby be with you.
-
vm_insnhelper.c (vm_callee_setup_keyword_arg,
vm_callee_setup_arg_complex): consider a hash argument for keyword
only when the number of arguments is more than the expected
mandatory parameters. [ruby-core:53199] [ruby-trunk - Bug #8040] -
test/ruby/test_keyword.rb: update a test for above.
Updated by ozydingo (Andrew Schwartz) over 8 years ago
- ruby -v changed from ruby 2.0.0p0 (2013-02-24 revision 39474) [x86_64-linux] to ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-darwin13]
This is unfortunately still an issue with default values in positional arguments:
2.2.2 > def foo(hash={}, opt: true); p hash; p opt; end
=> :foo
2.2.2 > foo({a: 1})
ArgumentError: unknown keyword: a
Expected behavior is that foo can be called with a hash argument in the first position without needing to specify the optional keyword args.
Yusuke Endoh wrote:
This issue was solved with changeset r40992.
Pablo, thank you for reporting this issue.
Your contribution to Ruby is greatly appreciated.
May Ruby be with you.
vm_insnhelper.c (vm_callee_setup_keyword_arg,
vm_callee_setup_arg_complex): consider a hash argument for keyword
only when the number of arguments is more than the expected
mandatory parameters. [ruby-core:53199] [ruby-trunk - Bug #8040]test/ruby/test_keyword.rb: update a test for above.
Updated by avit (Andrew Vit) over 8 years ago
- Backport deleted (
1.9.3: UNKNOWN, 2.0.0: UNKNOWN)
Andrew Schwartz wrote:
This is unfortunately still an issue with default values in positional arguments
See #11967 for the explanation.
Updated by hsbt (Hiroshi SHIBATA) over 6 years ago
- Related to Feature #14183: "Real" keyword argument added
Updated by TylerRick (Tyler Rick) about 6 years ago
ozydingo (Andrew Schwartz) wrote:
This is unfortunately still an issue with default values in positional arguments:
2.2.2 > def foo(hash={}, opt: true); p hash; p opt; end
=> :foo
2.2.2 > foo({a: 1})
ArgumentError: unknown keyword: aExpected behavior is that foo can be called with a hash argument in the first position without needing to specify the optional keyword args.
Ay! Just ran into this today.
Looks like this case is tracked in #12717 and #11967 but won't be fixed until Ruby 3 (#14183).
I wish there were a good workaround. Best I was able to come up with was:
def foo(hash={}, options = {opt: true})
opt = options[:opt]
[hash, opt]
end
> foo({a: 1})
=> [{:a=>1}, true]
> foo({a: 1}, opt: false)
=> [{:a=>1}, false]