Issue #14183 has been updated by mame (Yusuke Endoh).

File vm_args_v2.diff added

Jeremy,
I really appreciate you to use time for this issue.  And sorry for my late response.

I have misunderstood some points of your proposal, and now I feel that it is fairly good.  But please let me consider for a while...  This topic is really hard to exhaust corner cases.


> # My Proposed Alternative

Just confirm.  I think your following snippet lacks `unless kw.empty?`, right?

```
# Add keyword arguments
def foo(*args, output: $stdout, **kw)
  args << kw unless kw.empty? # This "unless" modifiler is needed, I think.
  output.puts args.inspect
end
foo(key: 42)
# => [{:key=>42}]
```

And, `foo({})` will assign `args = [{}]`, right?  If so, your proposal looks good enough to me.
Of course, if there was a call `foo(output: 42)` before adding keywords, the call will break.
That is unfortnate, but this may be a good compromise.


> # Issues with keyword-argument-separation branch

Thank you for checking my prototype deeply!

> The rb_no_keyword_hash approach breaks modification of the hash, which I believe is unexpected:

Yes.  Akr and I knew that this would bring some incompatibility.  We expected that the incompatibility should be small, but I noticed that it doesn't, unfortunately.  It should be fixed by something like special instance variable, as you said.

> The warning seems inconsistent. For positional splats, you get warned if the braceless hash is the first argument, but not if it is a subsequent argument:

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.

> Behavior is different for methods defined in C, as C methods are always passed a hash, so the brace, braceless, and splat forms all work:

We will keep the compatibility of C API because it would be more difficult to fix.  Regardless of whether the brace is used, C method consisitently receives a hash.
By the way, ko1 is now working on replacement of built-in methods from C to Ruby.  (He will talk about this plan in RubyKaigi: [Write a Ruby interpreter in Ruby for Ruby 3](https://rubykaigi.org/2019/presentations/ko1.html#apr18).)
His main motivation is performance, but this will also reduce the problem of C methods that receives keyword arguments.



And, thank you for your alternative patch.  I tried it with our internal Rails app again.  It emitted about 8k warnings (much less than 120k!).  Unfortunately I have no enough time to analyze the result, but it looks that no modification is required in our code base.  Great.

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

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