为什么 ** 在 "splatting" 关键字参数时是可选的?

Why is ** optional when "splatting" keyword arguments?

给定此方法定义:

def foo(a = nil, b: nil)
  p a: a, b: b
end

当我使用单个散列参数调用该方法时,散列总是隐式转换为关键字参数,无论 **:

hash = {b: 1}
foo(hash)     #=> {:a=>nil, :b=>1}
foo(**hash)   #=> {:a=>nil, :b=>1}

我可以传递另一个(空)散列作为解决方法:

foo(hash, {}) #=> {:a=>{:b=>1}, :b=>nil}

但是,这看起来很笨拙

我原以为 Ruby 会像处理数组一样处理这个问题,即:

foo(hash)     #=> {:a=>{:b=>1}, :b=>nil}
foo(**hash)   #=> {:a=>nil, :b=>1}

并使用文字:

foo({b: 1})   #=> {:a=>{:b=>1}, :b=>nil}
foo(b: 1)     #=> {:a=>nil, :b=>1}
foo(**{b: 1}) #=> {:a=>nil, :b=>1}

当前的实现看起来像一个缺陷,我期望它的工作方式似乎很明显。

这是一个被忽视的边缘案例吗?我不这么认为。没有以这种方式实施可能是有充分理由的。

请问有没有大佬解惑一下?

  1. 至于缺少**部分:

    我的猜测是,为了使方法调用简单,Ruby 总是一次将不带大括号的 key: value 形式解释为省略大括号的散列,无论它是否实际上将被解释为这样的散列或作为关键字参数。

    然后,为了将其解释为关键字参数,** 隐式应用于它。

    因此,如果您传递了一个显式散列,它不会对上述过程产生影响,并且有空间将其解释为实际散列或关键字参数。

    当您明确通过 ** 时会发生什么:

    method(**{key: value})
    

    是hash分解:

    method(key: value)
    

    then 被解释为带省略括号的散列:

    method({key: value})
    

    then 被解释为散列或关键字参数。

  2. 至于关键字参数优先于其他参数,请参见Ruby核心上的post:https://bugs.ruby-lang.org/issues/11967.