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


jeremyevans0 (Jeremy Evans) wrote:
> mame (Yusuke Endoh) wrote:
> > Good catch, I didn't intend it.  I fixed my branch.  And this brings more warnings ;-) so I re-examined our internal Rails app (explained later).  And the modification of my branch made your patch inapplicable, so I'm attaching a modified version of your patch.
> 
> Thank you, I will try to do some more testing with your revised branch and the modified patch next week.

mame,

With your revised branch, it looks like the the keyword argument separation for positional splats has already happened, and there is no warning.  Both your revised branch and your previous branch also already implement keyword argument separation for optional positional arguments without a warning. There is still a warning for the case where all positional arguments are required, though. 

Example code:


```ruby
def foo(a, *b, **c)
  [a, b, c]
end

def bar(a, b=1, **c)
  [a, b, c]
end

def baz(a, **c)
  [a, c]
end
```

Your revised branch (commit 73a9633114ef00bf793d7ca39e49f24448499487)

```ruby
foo(1, {a: 1})
# => [1, [{:a=>1}], {(NO KEYWORD)}]

bar(1, {a: 1})
# => [1, {:a=>1}, {(NO KEYWORD)}]

baz(1, {a: 1})
# warning: The last argument for `baz' (defined at (irb):9) is used as the keyword parameter
# => [1, {:a=>1}]
```

Your previous branch (commit 3903e75678eca4874e3122a42bd073b018f9458e):

```ruby
foo(1, {a: 1})
# warning: The last argument for `foo' (defined at (irb):15) is used as the keyword parameter    
# => [1, [], {:a=>1}]

bar(1, {a: 1})
# => [1, {:a=>1}, {(NO KEYWORD)}]

baz(1, {a: 1})
# warning: The last argument for `baz' (defined at (irb):9) is used as the keyword parameter
# => [1, {:a=>1}]
```

Ruby 2.6:

```ruby
foo(1, {a: 1})
# => [1, [], {:a=>1}]

bar(1, {a: 1})
# => [1, 1, {:a=>1}]

baz(1, {a: 1})
# => [1, {:a=>1}]
```

I believe the expected behavior in Ruby 2.7 is to warn but return the same results as Ruby 2.6 in all three cases, is that correct?

I applied your vm_args_v2.diff on top of your revised branch, and also removed the rb_no_keyword_hash variable and related handling.  No compilation issues, and some basic tests work, but many stdlib tests fail due to the keyword argument separation already being applied for methods that use positional splats (mostly tmpdir and csv).  

To make testing easier, I uploaded my branch GitHub: https://github.com/jeremyevans/ruby/commits/keyword-argument-separation

After the issues with positional splats and optional positional arguments are fixed, I'll rebase my patch on top of that.  Note that my branch does not include your changes to the standard library and tests to avoid warnings.  I believe the changes required to the standard library and tests should be much less extensive with my proposal, and I would like to only make the minimum changes necessary.  I want to make sure the branch does not cause any failures before attempting to remove warnings.

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

* 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>