Feature #20508
openExplicit access to *, **, &, and ...
Description
I find debugging and certain meta-programming tasks challenging because there's no explicit APIs for accessing certain types of arguments in Ruby.
Here's some example code:
def splats(one, *, two: nil, **, &)
# These work
p(*)
p(**)
# But the block doesn't show a proc.
p(&)
# This would show [:one, :two, :_]
p binding.local_variables
end
splats(:arg, two: true) do
42
end
I'm not sure how to access the &
variable, unless I name it. The same applies to endless ruby methods.
I'd like to see a way to explicitly call bindings for those methods. Something like this (not sure if binding is the right place for it):
def splats(one, *, true: nil, **, &)
binding.arguments # Returns an array
binding.keyword_arguments # Returns a hash
binding.block_argument # Returns a block
end
These methods would give me access to the values used to call the method.
Updated by jeremyevans0 (Jeremy Evans) 6 months ago
Implementing this would require removing recent and planned optimizations to avoid allocations that take advantage of the fact that *
and **
are not directly accessible as objects, only passable as splats.
I'm not sure how to access the
&
variable, unless I name it. The same applies to endless ruby methods.
You can already do this for the block argument:
block_argument = ->(&b){b}.(&)
For the *
and **
arguments, you cannot get direct access to them, but you can easily make copies of them. If you want all of *
, **
, and &
:
arguments, keyword_arguments, block_argument = ->(*a, **kw, &b){[a, kw, b]}.(*, **, &);
Updated by Eregon (Benoit Daloze) 6 months ago
Implementing this would require removing recent and planned optimizations to avoid allocations that take advantage of the fact that
*
and**
are not directly accessible as objects, only passable as splats.
What if these methods would return a new Array/Hash/a copy of the real one?
I'm not sure whether it's a good idea to add those methods but it does feel weird there is no easy way to display these variables in a debugger showing arguments by default.
I suppose that eval trick is good enough though for that use case, although it seems a lot more expensive than Binding methods (a debugger would need the binding anyway and so already have it).
Another issue with these methods is it does not seem clear if Binding#arguments would return all positional arguments or only the value of *
. Same for kwargs. I suppose it's the latter, but that means the names are rather confusing then.
Updated by zverok (Victor Shepelev) 6 months ago
Regardless of the rest of the proposal,
I'm not sure how to access the
&
variable, unless I name it.
There is a way:
def show_block(&)
p proc(&)
end
show_block {} #=> #<Proc:0x00007f485eb07e28 anon_block.rb:5>
show_block &-> {} #=> #<Proc:0x00007f485eb07c98 anon_block.rb:6 (lambda)>
show_block &:to_i #=> #<Proc:0x00007f485eb07ba8(&:to_i) (lambda)>
show_block &method(:puts) #=> #<Proc:0x00007f485eb07a18 (lambda)>
show_block #=> in `proc': tried to create Proc object without a block (ArgumentError)
Depending on the goals, that might be enough for some introspection.
Updated by matz (Yukihiro Matsumoto) 4 months ago
I am not sure why you don't want to name arguments, where simple a
, b
, c
might work. Elaborate the motivation behind the proposal.
Otherwise, I am against the proposal. It does not worth the effort (and hindering the future optimization).
Matz.