Issue #16519 has been updated by Dan0042 (Daniel DeLorme).


Eregon (Benoit Daloze) wrote in #note-5:
> * When the keyword Hash is created out of nothing, i.e., by `def m(**hash_created_by_double_splat)` and `m` was not passed any keyword argument, we could remember that, and when calling another method with `foo(**hash_created_by_double_splat)` remove that Hash, since the user never intended it. For other cases, keep to pass the keyword Hash just like for non-empty keyword hashes, since the user passed keywords explicitly.

This is the approach I used in my proof of concept branch, and I find it works pretty well in practice.

---

jeremyevans0 (Jeremy Evans) wrote in #note-6:
> This is by design.  Empty keyword splats are equivalent to passing no arguments, just as empty regular splats are equivalent to passing no arguments.  I consider the Ruby <2.7 behavior a bug in this regard.  When you consider that `**{}` never passed arguments (was removed by the parser), I think the intended behavior for empty keyword splats was not to pass arguments.

When you say "passing no arguments" I think you're conflating keyword and positional. An empty hash splat should pass no _keyword_ arguments, just as an array splat should pass no _positional_ arguments. So I would agree with you _IF_ this was complete and strict separation of keyword/positional, and then we'd get this:

```ruby
def foo(a=42); a; end
foo(**{})    #=> 42
foo(**{x:1}) #=> error
```

But here we're talking about the keyword-to-positional compatibility behavior, the one that you championed in #14183. Since this is for the sake of compatibility, it doesn't make sense to introduce incompatibility here. It's also very inconsistent to mix "strict" behavior for empty hashes and compatibility behavior for non-empty. Consider these bugs:

```ruby
def foo(opts); opts; end
h={}
h[:k] = 1 if condition=false
p foo(**h)  #=> {:k=>1} if condition is true
            #=> warning (and eventually error) if condition is false

def bar(*args)
  opts = args.pop if args.last.is_a?(Hash)
  args
end
data = {"k"=>42}
options = {d:5}
bar(data, **options) #=> [] if options is empty; data was taken as opts!
bar(data, **options) #=> [{"k"=>42}] if options is non-empty
```


----------------------------------------
Bug #16519: pp [Hash.ruby2_keywords_hash({})] shows `[nil]`
https://bugs.ruby-lang.org/issues/16519#change-84123

* Author: Eregon (Benoit Daloze)
* Status: Closed
* Priority: Normal
* ruby -v: ruby 2.8.0dev (2020-01-21T13:45:10Z master 5798d35ff6) [x86_64-linux]
* Backport: 2.5: UNKNOWN, 2.6: UNKNOWN, 2.7: REQUIRED
----------------------------------------
This happens on `master`:

```
$ ruby -ve 'ruby2_keywords def flag(*a); a.last; end; pp [flag(**{})]'
ruby 2.8.0dev (2020-01-21T13:45:10Z master 5798d35ff6) [x86_64-linux]
[nil]
```

Of course it should be `[{}]`, as it is for `pp [{}]`.

On 2.7.0 it warns (should be fixed, it's valid to `pp` a flagged Hash):
```
$ ruby -ve 'ruby2_keywords def flag(*a); a.last; end; pp [flag(**{})]'
ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-linux]
[/home/eregon/.rubies/ruby-2.7.0/lib/ruby/2.7.0/pp.rb:226: warning: Passing the keyword argument as the last hash parameter is deprecated
/home/eregon/.rubies/ruby-2.7.0/lib/ruby/2.7.0/pp.rb:334: warning: The called method is defined here
{}]
```

The warning being in the middle of the output is a fun fact here.
Lines it refers to (still the same on current master):
https://github.com/ruby/ruby/blob/v2_7_0/lib/pp.rb#L226
https://github.com/ruby/ruby/blob/v2_7_0/lib/pp.rb#L334

This is very confusing as it can happen during `test-all` and then show output such as:
```
<[{:a=>1}]> expected but was
<[{:a=>1}, nil]>.
```
when the reality is (can be verified with `p` before the `assert_equal`):
```
<[{:a=>1}]> expected but was
<[{:a=>1}, {}]>.
```



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