Feature #20349
openPattern Matching - Expose local variable captures
Description
In Regular Expressions we have the ability to utilize Regexp.last_match
(link) to access the most recent match data from a regular expression match. I would like to propose that we have similar functionality for pattern matching:
require "rubocop"
def ast_from(string)
case string
when String then processed_source_from(string).ast
when RuboCop::ProcessedSource then string.ast
when RuboCop::AST::Node then string
else nil
end
end
def longform_block?(node)
node in [:block, # s(:block,
[:send, target, target_method], # s(:send, s(:array), :select),
[[:arg, v]], # s(:args, s(:arg, :v)),
[:send, [:lvar, ^v], block_method] # s(:send, s(:lvar, :v), :even?))
]
end
longform_block?(ast_from("[1, 2, 3].select { |v| v.even? }"))
# => true
# Name is not important, idea is, name can be debated. `source` is used to not
# show AST nodes
PatternMatch.last_match.transform_values(&:source)
# => {:node=>"[1, 2, 3].select { |v| v.even? }",
# :result=>"[1, 2, 3].select { |v| v.even? }",
# :target=>"[1, 2, 3]",
# :target_method=>:select,
# :v=>:v,
# :block_method=>:even?}
# Hacky version to show how / where behavior could be captured
def longform_block?(node)
result = node in [:block, # s(:block,
[:send, target, target_method], # s(:send, s(:array), :select),
[[:arg, v]], # s(:args, s(:arg, :v)),
[:send, [:lvar, ^v], block_method] # s(:send, s(:lvar, :v), :even?))
]
pp(binding.local_variables.to_h { [_1, binding.local_variable_get(_1).then { |s| s.source rescue s }] })
result
end
Updated by Eregon (Benoit Daloze) 8 months ago
This would make every pattern matching a lot slower which seems a big no-no performance wise.
I think that's enough to reject this proposal.
Because it would force to create that binding/Hash eagerly in case PatternMatch.last_match
is used later.
Actually, $~
/Regexp.last_match
and $_
are already causing significant performance problems for Ruby implementations, so I am strongly against any more of these "magically create variables in the caller".
BTW the example doesn't show how this is useful, is it to print the current state for debugging?
If so a helper method taking a binding
seems appropriate (called inside the longform_block?
method, not outside).
Updated by nobu (Nobuyoshi Nakada) 8 months ago
baweaver (Brandon Weaver) wrote:
PatternMatch.last_match.transform_values(&:source)
Your proposal is PatternMatch.last_match
, right?
# => {:node=>"[1, 2, 3].select { |v| v.even? }", # :result=>"[1, 2, 3].select { |v| v.even? }", # :target=>"[1, 2, 3]", # :target_method=>:select, # :v=>:v, # :block_method=>:even?}
Why :node
and :result
which are not involved in the pattern matching are contained?
pp(binding.local_variables.to_h { [_1, binding.local_variable_get(_1).then { |s| s.source rescue s }] })
You look like just want a method to make this local variable hash, to me.
Updated by Eregon (Benoit Daloze) 8 months ago
nobu (Nobuyoshi Nakada) wrote in #note-2:
You look like just want a method to make this local variable hash, to me.
Binding#to_h
or similar would be convenient.