Project

General

Profile

Feature #16456

Ruby 2.7 argument delegation (...) should be its own kind of parameter in Method#parameters

Added by aaronc81 (Aaron Christiansen) about 2 months ago. Updated about 2 months ago.

Status:
Open
Priority:
Normal
Assignee:
-
Target version:
-
[ruby-core:96508]

Description

A method defined with ... as its parameter list is equivalent to one defined with *args, &blk, according to Method#parameters.

def foo(...); end
p method(:foo).parameters
# => [[:rest, :*], [:block, :&]]

Even in Ruby 2.7, ... and *args, &blk are not quite equivalent as the latter may produce a warning where the former does not. In Ruby 3.0 and beyond, ... and *args, &blk will have a substantial semantic difference. Due to this, I don't consider the current behaviour of Method#parameters particularly ideal when dealing with methods using this new syntax.

If the goal of ... is to be a "delegate everything" operator, even when parameter passing is changed like in Ruby 3.0, I would propose that Method#parameters considers it a unique type of parameter. For example:

def foo(...); end
p method(:foo).parameters
# => [[:delegate, :"..."]]
#1

Updated by aaronc81 (Aaron Christiansen) about 2 months ago

  • Description updated (diff)
#2

Updated by aaronc81 (Aaron Christiansen) about 2 months ago

  • Description updated (diff)

Updated by Eregon (Benoit Daloze) about 2 months ago

I think it should be:

[[:rest, :*], [:keyrest, :**], [:block, :&]]

because that's what it will act like in Ruby 3.0+.

Is there an advantage to have its own type of parameter?
That would make usages of #parameters more complex for I think very little gain.

Updated by zverok (Victor Shepelev) about 2 months ago

I think it should be:

[[:rest, :*], [:keyrest, :**], [:block, :&]]

(I have a deja vu we already discussed it :)))

Names are redundant, it should be just

[[:rest], [:keyrest], [:block]]

Like this:

def foo(*, **, &b)
end

p method(:foo).parameters
# => [[:rest], [:keyrest], [:block, :b]]

(Not sure about "block" parameter -- unnamed block parameters aren't existing in current Ruby)

Updated by aaronc81 (Aaron Christiansen) about 2 months ago

Is there an advantage to have its own type of parameter?

I believe the advantages of doing this are:

  • If Ruby ever introduces a new type of parameter, the result of #parameters won't need to change for existing code which uses ..., making upgrades easier. This is especially important if ... is designed as a future-proof way of delegation, as then it seems important that its behaviour shouldn't change between versions.
  • It could be useful for introspection to be able to differentiate between the two. For example, this could allow a complex DSL to assign a special meaning to ....

Updated by Dan0042 (Daniel DeLorme) about 2 months ago

In the future it will be possible to combine ... with other parameters. So if we think about what parameters would return in cases like these...

method(def foo(a, *args, ...); end).parameters
#1=> [[:req, :a], [:rest, :args], [:delegate]]
#2=> [[:req, :a], [:rest, :args], [:keyrest], [:block]]
#3=> [[:req, :a], [:rest, :args], [:rest], [:keyrest], [:block]]

method(def foo(a, **kw, ...); end).parameters
#1=> [[:req, :a], [:keyrest, :kw], [:delegate]]
#2=> [[:req, :a], [:keyrest, :kw], [:rest], [:block]]
#3=> [[:req, :a], [:keyrest, :kw], [:rest], [:keyrest], [:block]]

I see the point of wanting to know if the method signature includes ... or not, but I don't think I like the idea of having a :delegate that can mean different things.

What about this?
[[:rest, :"..."], [:keyrest, :"..."], [:block, :"..."]]

Updated by aaronc81 (Aaron Christiansen) about 2 months ago

I think that the [[:rest, :"..."], [:keyrest, :"..."], [:block, :"..."]] solution looks like a good option, as it keeps roughly the same behaviour while adding the differentiation between *args, &blk and ....

Also available in: Atom PDF