Issue #14183 has been updated by jeremyevans0 (Jeremy Evans).


Dan0042 (Daniel DeLorme) wrote:
> ```
> def foo(**f); p [f]; end
> foo({})
> warning: The last argument for `foo' (defined at kwtest.rb:4110) is used as the keyword parameter
> [{}]
> #but if it was not used as keyword parameter we'd get "wrong number of arguments" error
> ```

Correct.  That is expected, as the method takes no arguments, only keyword parameters.

> ```
> def foo(b=5, e:15); p [b,e]; end
> foo(9=>9)
> warning: The keyword argument for `foo' (defined at kwtest.rb:73776) is passed as the last hash parameter
> [{9=>9}, 15]
> #but if it was not passed as the last hash parameter we'd get "unknown keyword" error
> ```

Correct.  This is expected, as the method does not support a keyword parameter for `9`.

> ```
> def foo(a, b=5, d:); p [a,b,d]; end
> foo(0, 9=>9, d:10)
> warning: The last argument for `foo' (defined at kwtest.rb:213108) is split into positional and keyword parameters
> [0, {9=>9}, 10]
> #but if it wasn't split we'd get "unknown keyword" error
> ```

Correct.  This is expected, as the method does not support a keyword parameter for `9`.

> ```
> def foo(*a, **o); p [a,o]; end
> a = [1,2,3,{x:1}]
> foo(*a)
> (irb):6: warning: The last argument for `foo' (defined at (irb):1) is used as the keyword parameter
> [[1, 2, 3], {:x=>1}]
> ```

This warning is expected, because behavior will change in Ruby 3 to return `[[1, 2, 3, {:x=>1}], {}]`

> Am I correct in assuming these warnings will be errors in ruby 3?

Not the last one, since it a behavior change but would not cause an error in the example given.

> But it seems to me there's no need to raise an error since none of those have any ambiguity. Wouldn't it be better here for ruby to just "do the right thing"?

That's what Ruby has been trying to do for a long time.  You probably want to see all of the referenced issues to see why it isn't a good idea, or at least understand what problems it causes.

> The last one in particular seems problematic. Using `*args` to pass arguments to another method is a common pattern. All proxy objects do that. If the receiver happens to have keyword arguments and it breaks, that means we'd be required to always specify `*args, **opts` for all proxying. That's going to break a lot of code.

This is correct.  Generic method forwarding will require `*args, **opts, &block` in Ruby 3.  We are aware that this change will break forwarding to methods that accept keyword arguments if you just use `*args, &block`.  However, it should not break forwarding to methods that do not accept keyword arguments.

----------------------------------------
Feature #14183: "Real" keyword argument
https://bugs.ruby-lang.org/issues/14183#change-81235

* Author: mame (Yusuke Endoh)
* Status: Open
* Priority: Normal
* Assignee: 
* Target version: Next Major
----------------------------------------
In RubyWorld Conference 2017 and RubyConf 2017, Matz officially said that Ruby 3.0 will have "real" keyword arguments.  AFAIK there is no ticket about it, so I'm creating this (based on my understanding).

In Ruby 2, the keyword argument is a normal argument that is a Hash object (whose keys are all symbols) and is passed as the last argument.  This design is chosen because of compatibility, but it is fairly complex, and has been a source of many corner cases where the behavior is not intuitive.  (Some related tickets: #8040, #8316, #9898, #10856, #11236, #11967, #12104, #12717, #12821, #13336, #13647, #14130)

In Ruby 3, a keyword argument will be completely separated from normal arguments.  (Like a block parameter that is also completely separated from normal arguments.)
This change will break compatibility; if you want to pass or accept keyword argument, you always need to use bare `sym: val` or double-splat `**` syntax:

```
# The following calls pass keyword arguments
foo(..., key: val)
foo(..., **hsh)
foo(..., key: val, **hsh)

# The following calls pass **normal** arguments
foo(..., {key: val})
foo(..., hsh)
foo(..., {key: val, **hsh})

# The following method definitions accept keyword argument
def foo(..., key: val)
end
def foo(..., **hsh)
end

# The following method definitions accept **normal** argument
def foo(..., hsh)
end
```

In other words, the following programs WILL NOT work:

```
# This will cause an ArgumentError because the method foo does not accept keyword argument
def foo(a, b, c, hsh)
  p hsh[:key]
end
foo(1, 2, 3, key: 42)

# The following will work; you need to use keyword rest operator explicitly
def foo(a, b, c, **hsh)
  p hsh[:key]
end
foo(1, 2, 3, key: 42)

# This will cause an ArgumentError because the method call does not pass keyword argument
def foo(a, b, c, key: 1)
end
h = {key: 42}
foo(1, 2, 3, h)

# The following will work; you need to use keyword rest operator explicitly
def foo(a, b, c, key: 1)
end
h = {key: 42}
foo(1, 2, 3, **h)
```

I think here is a transition path:

* Ruby 2.6 (or 2.7?) will output a warning when a normal argument is interpreted as keyword argument, or vice versa.
* Ruby 3.0 will use the new semantics.

---Files--------------------------------
vm_args.diff (4.19 KB)
vm_args_v2.diff (4.18 KB)


-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request / ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>