Feature #19134
closed** is not allowed in def foo(...)
Description
*
and &
are allowed in the body of a method with ...
argument forwarding, but **
is not allowed.
def foo(...)
bar(*) # OK
baz(&) # OK
quux(**) # NG
end
Is it intended behavior?
It seems that parse.y has code like #ifdef RUBY3_KEYWORDS
, and if RUBY3_KEYWORDS, **
will also be supported.
Updated by shugo (Shugo Maeda) almost 2 years ago
I've created a pull request: https://github.com/ruby/ruby/pull/6818
With this change a test of rbs.gem fails because argument types of def foo(...)
is changed to (*untyped, **untyped **)
from (*untyped)
, but I believe it's right.
(however, the specifal variable name **
should be hidden by rbs.gem.)
I've created a issue: https://github.com/ruby/rbs/issues/1163
Updated by matz (Yukihiro Matsumoto) almost 2 years ago
LGTM.
Matz.
Updated by shugo (Shugo Maeda) almost 2 years ago
- Status changed from Open to Closed
Applied in changeset git|4fc668a4f3b9b67cc7566096ab55cab34c67c158.
Allow ** in def foo(...)
[Feature #19134]
Updated by shugo (Shugo Maeda) almost 2 years ago
matz (Yukihiro Matsumoto) wrote in #note-2:
LGTM.
Thank you. I've merged it.
I realized that my fix also chaged the behavior of the following code:
def foo(*, **, &)
bar(...)
end
def bar(*args, **kw, &block)
p [args, kw, block&.call]
end
foo(1, 2, x: 3, y: 4) { 5 }
My fix changed the result from [[1, 2], {}, 5]
to [[1, 2], {:x=>3, :y=>4}, 5]
.
So ...
is now a syntax sugar of *, **, &
.
Updated by Eregon (Benoit Daloze) almost 2 years ago
IMHO if ...
is used then *
, **
and &
should all be forbidden (a SyntaxError at parse time).
Because that way is the best for optimizing delegation.
And also taking apart *
and **
is arguably not really delegation anymore.
Updated by Eregon (Benoit Daloze) almost 2 years ago
- Related to Bug #19165: Method (with no param) delegation with *, **, and ... is slow added
Updated by Eregon (Benoit Daloze) almost 2 years ago
Although, it should still be possible to optimize delegation as good as possible and allow *
/**
/&
by having those behave like def args(*,**,&) = *
, def kwargs(*,**,&) = **
, def block(*,**,&) = &
and as if *
was replaced by args(...)
, etc, but that's of course at the expense of making the handling of those slightly slower and more complicated (but at least it doesn't slow down delegation via (...)
).
Updated by shugo (Shugo Maeda) almost 2 years ago
Eregon (Benoit Daloze) wrote in #note-5:
IMHO if
...
is used then*
,**
and&
should all be forbidden (a SyntaxError at parse time).
Because that way is the best for optimizing delegation.
And also taking apart*
and**
is arguably not really delegation anymore.
&
is allowed in 3.1, so it's a breaking change to prohibit it.
Updated by shugo (Shugo Maeda) almost 2 years ago
- Status changed from Closed to Open
I think it's most conservative to probhibit *
and **
:
def foo(...)
bar(&) # OK
baz(*) # error
quux(**) # error
end
And an error should occur in the following code instead of dropping keyword arguments:
def foo(*, **, &)
bar(...)
end
def bar(*args, **kw, &block)
p [args, kw, block&.call]
end
foo(1, 2, x: 3, y: 4) { 5 }
What do you think, Matz?
Updated by Eregon (Benoit Daloze) almost 2 years ago
shugo (Shugo Maeda) wrote in #note-8:
&
is allowed in 3.1, so it's a breaking change to prohibit it.
Right and &
is not problematic re performance, it's a completely separate argument anyway.
I agree with your proposal in your comment just above this one.
Updated by mame (Yusuke Endoh) almost 2 years ago
IMHO if ... is used then *, ** and & should all be forbidden (a SyntaxError at parse time).
I agree with this. If you want to do that, you should take it with def foo(*, **, &)
.
I feel that only &
is acceptable since a block is implicitly passed. But I don't think *
and **
make sense inside def foo(...)
.
Updated by shugo (Shugo Maeda) almost 2 years ago
- Status changed from Open to Closed
Applied in changeset git|2581de112c1957dc4b5852e54337551dc8972c99.
Disallow mixed usage of ... and /*
[Feature #19134]
Updated by shugo (Shugo Maeda) almost 2 years ago
mame (Yusuke Endoh) wrote in #note-12:
I feel that only
&
is acceptable since a block is implicitly passed. But I don't think*
and**
make sense insidedef foo(...)
.
Speaking of implicity of blocks, def f; g(&); end
causes a syntax error, so it may be better to allow it, or disallow def f(...); g(&); end
in the future versions.
Anyway, I've fixed the behavior of ...
as described in #note-9.
Updated by Eregon (Benoit Daloze) almost 2 years ago
shugo (Shugo Maeda) wrote in #note-14:
Speaking of implicity of blocks,
def f; g(&); end
causes a syntax error, so it may be better to allow it, or disallowdef f(...); g(&); end
in the future versions.
Since some time @ko1 (Koichi Sasada) worked on no implicit block if not a parameter of the method (with the exception of yield
in the body). I think that's a good property, so I think it's good for def f; g(&); end
to be a syntax error.
For def f(...); g(&); end
"..." is "capture all arguments, positional, keyword and block" so I think it's more natural there to be able to access the block (although I can't think of when that would be useful, maybe def f(...); g(&); h(...) end
when h
is known to ignore the block but that feels hacky, better use (*, **, &)
parameters then).
Anyway, I've fixed the behavior of
...
as described in #note-9.
Thanks!