Issue #19286 has been updated by hmdne (hmdne -).
kwargs are... complicated. Let me first extend the issue with additional versions of the
above (I run Ruby 3.1, but from what I know, everything applies to anything >= Ruby
3.0):
```ruby
[1] pry(main)> method(def a(a:, b:); end).arity
=> 1
[2] pry(main)> method(def a(a:, b: 5); end).arity
=> 1
[3] pry(main)> method(def a(a: 5, b: 5); end).arity
=> -1
[4] pry(main)> method(def a(**kwargs); end).arity
=> -1
[5] pry(main)> method(def a(**nil); end).arity
=> 0
[6] pry(main)>
```
So, basically, how I understand the kwargs: they are actually internally the last Hash
argument of a function (that is passed with a special flag). If all kwargs are optional,
then it's an optional argument (so arity is -1).
They are unlike how blocks work - it's not a separate category of arguments.
If a function is declared with kwargs arguments special syntax (later I will call it a
kwargs-syntax function), then this flag is enforced while checking arity (since Ruby 3.0 I
believe). But otherwise, it's not, and kwargs are just passed as a simple argument:
```ruby
[8] pry(main)> def a(kwargs); kwargs; end; a(a: 5)
=> {:a=>5}
[9] pry(main)> def a(kwargs = {}); kwargs; end; a(a: 5)
=> {:a=>5}
[10] pry(main)> def a(*args); args; end; a(a: 5)
=> [{:a=>5}]
[11] pry(main)>
```
It is possible to call a kwargs-syntax function with a non-kwargs Hash argument (but only
with restarg syntax), if we set a flag (a distinct one, from what I understand) using
Hash.ruby2_keywords_hash.
```ruby
[15] pry(main)> def a(**kwargs); kwargs; end; a(*[Hash.ruby2_keywords_hash({a: 5})])
=> {:a=>5}
[16] pry(main)>
```
So, in a way, the first list of examples would be equivalent in terms of arity to the
following set of examples:
```ruby
[1] pry(main)> method(def a(kwargs); end).arity
=> 1
[2] pry(main)> method(def a(kwargs); end).arity
=> 1
[3] pry(main)> method(def a(kwargs = {}); end).arity
=> -1
[4] pry(main)> method(def a(kwargs = {}); end).arity
=> -1
[5] pry(main)> method(def a(); end).arity
=> 0
[6] pry(main)>
```
I think a better interface to get the argument signature of a function is
`Method#parameters`:
```ruby
[18] pry(main)> method(def a(kwargs); end).parameters
=> [[:req, :kwargs]]
[19] pry(main)> method(def a(**kwargs); end).parameters
=> [[:keyrest, :kwargs]]
[20] pry(main)> method(def a(a:, b:); end).parameters
=> [[:keyreq, :a], [:keyreq, :b]]
[21] pry(main)> method(def a(a: 5, b: 7); end).parameters
=> [[:key, :a], [:key, :b]]
[22] pry(main)> method(def a(kwargs = {}); end).parameters
=> [[:opt, :kwargs]]
[23] pry(main)> method(def a(**nil); end).parameters
=> [[:nokey]]
[24] pry(main)>
```
(My understanding on this topic stems from a fact, that I am working on updating kwargs
support in Opal to match MRI behavior)
----------------------------------------
Bug #19286: What should kwargs' arity be?
https://bugs.ruby-lang.org/issues/19286#change-100890
* Author: matsuda (Akira Matsuda)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.3.0dev (2022-12-28T16:43:05Z master cada537040) +YJIT [arm64-darwin21]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Hello, guys. It's time for a quick Ruby quiz.
Q: What is this method's arity?
def f(a:, b:)
end
It requires two arguments, hence it should be 2?
Or if we call this method with one argument, the error message says "wrong number of
arguments (given 1, expected 0; required keywords: a, b) (ArgumentError)", which
means the arity is 0, maybe?
A: The answer is,
$ all-ruby -e 'p method(def f(a:, b:) end).arity'
ruby-2.1.0-preview1 0
...
ruby-2.1.0 0
ruby-2.1.1 -1
ruby-2.1.2 1
...
ruby-3.1.0 1
it's been 1 since 2.1.2. But why 1? Why not 2 nor 0?
I asked this question to the ruby-core people, and ko1's answer was that even he has
no idea what the number 1 means.
![](random_-_ruby-lang_-_Slack.png)
So I thought it'd be worth asking this question here.
---Files--------------------------------
random_-_ruby-lang_-_Slack.png (40.1 KB)
--
https://bugs.ruby-lang.org/