[ruby-core:117233] [Ruby master Feature#20349] Pattern Matching - Expose local variable captures

Issue #20349 has been reported by baweaver (Brandon Weaver). ---------------------------------------- Feature #20349: Pattern Matching - Expose local variable captures https://bugs.ruby-lang.org/issues/20349 * Author: baweaver (Brandon Weaver) * Status: Open ---------------------------------------- In Regular Expressions we have the ability to utilize `Regexp.last_match` ([link](https://ruby-doc.org/3.2.2/Regexp.html#method-c-last_match)) 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: ```ruby 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 ``` -- https://bugs.ruby-lang.org/

Issue #20349 has been updated by Eregon (Benoit Daloze). 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). ---------------------------------------- Feature #20349: Pattern Matching - Expose local variable captures https://bugs.ruby-lang.org/issues/20349#change-107322 * Author: baweaver (Brandon Weaver) * Status: Open ---------------------------------------- In Regular Expressions we have the ability to utilize `Regexp.last_match` ([link](https://ruby-doc.org/3.2.2/Regexp.html#method-c-last_match)) 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: ```ruby 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 ``` -- https://bugs.ruby-lang.org/

Issue #20349 has been updated by nobu (Nobuyoshi Nakada). baweaver (Brandon Weaver) wrote:
PatternMatch.last_match.transform_values(&:source)
Your proposal is `PatternMatch.last_match`, right?
```ruby # => {: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?
```ruby 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. ---------------------------------------- Feature #20349: Pattern Matching - Expose local variable captures https://bugs.ruby-lang.org/issues/20349#change-107336 * Author: baweaver (Brandon Weaver) * Status: Open ---------------------------------------- In Regular Expressions we have the ability to utilize `Regexp.last_match` ([link](https://ruby-doc.org/3.2.2/Regexp.html#method-c-last_match)) 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: ```ruby 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 ``` -- https://bugs.ruby-lang.org/

Issue #20349 has been updated by Eregon (Benoit Daloze). 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. ---------------------------------------- Feature #20349: Pattern Matching - Expose local variable captures https://bugs.ruby-lang.org/issues/20349#change-107349 * Author: baweaver (Brandon Weaver) * Status: Open ---------------------------------------- In Regular Expressions we have the ability to utilize `Regexp.last_match` ([link](https://ruby-doc.org/3.2.2/Regexp.html#method-c-last_match)) 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: ```ruby 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 ``` -- https://bugs.ruby-lang.org/
participants (3)
-
baweaver (Brandon Weaver)
-
Eregon (Benoit Daloze)
-
nobu (Nobuyoshi Nakada)