Project

General

Profile

Actions

Feature #20508

open

Explicit access to *, **, &, and ...

Added by bradgessler (Brad Gessler) about 1 month ago. Updated about 1 month ago.

Status:
Open
Assignee:
-
Target version:
-
[ruby-core:118010]

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) about 1 month 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) about 1 month 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) about 1 month 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.

Actions

Also available in: Atom PDF

Like1
Like0Like0Like0