From: "matz (Yukihiro Matsumoto)" Date: 2022-06-09T03:30:11+00:00 Subject: [ruby-core:108811] [Ruby master Feature#18821] Expose Pattern Matching interfaces in core classes Issue #18821 has been updated by matz (Yukihiro Matsumoto). I agree with part of the proposal. Set and OpenStruct do not have order of elements, so it's not suitable for converting to arrays (ordered collections), where others are OK. Matz. ---------------------------------------- Feature #18821: Expose Pattern Matching interfaces in core classes https://bugs.ruby-lang.org/issues/18821#change-97886 * Author: baweaver (Brandon Weaver) * Status: Open * Priority: Normal ---------------------------------------- ## Problem Statement Pattern matching is an exceptionally powerful feature in modern versions of Ruby, but it has one critical weakness that we should discuss: It is only as powerful as the number of classes which implement its interfaces. The more common these interfaces become, the more powerful pattern matching will become for everyday use in any scenario. ## Areas of Attention That said, what are some classes in core Ruby where it may make sense to implement pattern matching interfaces, and what do we gain from them? I will provide an abbreviated list, but can look to qualify a larger list of potentials if this is of interest. ### Set Currently `Set` does not implement `deconstruct`. Especially `Enumerable`-like and `Array` like entities make sense here: ```ruby # Hypothetical implementation class Set alias_method :deconstruct, :to_a end Set[1, 2, 3] in [1, 2, *] # => true ``` ### Matrix Speaking of Array-like structures, Matrix may make sense as well: ```ruby class Matrix alias_method :deconstruct, :to_a end # => :deconstruct Matrix[[25, 93], [-1, 66]] in [[20..30, _], [..0, _]] # => true ``` ### CSV In the case of headers especially this can become very powerful with `deconstruct_keys`: ```ruby require "csv" require "net/http" require "json" # Hypothetical implementation class CSV::Row def deconstruct_keys(keys) # Symbol/String is contentious, yes, I will address in a moment self.to_h.transform_keys(&:to_sym) end end # Creating some sample data for example: json_data = URI("https://jsonplaceholder.typicode.com/todos") .then { Net::HTTP.get(_1) } .then { JSON.parse(_1, symbolize_names: true) } headers = json_data.first.keys rows = json_data.map(&:values) # Yes yes, hacky csv_data = CSV.generate do |csv| csv << headers rows.each { csv << _1 } end.then { CSV.parse(_1, headers: true) } # But can provide very interesting results: csv_data.select { _1 in userId: "1", completed: "true" }.size # => 11 ``` Though this one does raise the broader question of the conflation of Symbol and String keys for our convenience. Given that Ruby has a habit of coercing between the two in other cases I do not find this to be against the spirit of Ruby. ### RegExp MatchData In a similar line of thinking to the CSV I believe this would present interesting opportunities, though does raise the question of what to do with `nil` types (perhaps return `[]` and `{}` respectively? May be hacky though) ```ruby class MatchData alias_method :deconstruct, :to_a def deconstruct_keys(keys) named_captures.transform_keys(&:to_sym).slice(*keys) end end IP_REGEX = / (?\d{1,3})\. (?\d{1,3})\. (?\d{1,3})\. (?\d{1,3}) /x '192.168.1.1'.match(IP_REGEX) in { first_octet: '198', fourth_octet: '1' } # => true ``` As with before though, we do risk setting a precedent on the conflation of Symbol and String keys when it is convenient to us, so may be worth proceeding with caution there. ### OpenStruct Much like `Struct` I believe there's a good case to make here: ```ruby class OpenStruct def deconstruct_keys(keys) = keys ? to_h.slice(*keys) : to_h end me = OpenStruct.new(name: 'Brandon', age: 31) me in { name: /^B/ } # => true ``` ## Other Thoughts I believe there is great potential in the core of Ruby to spread the pattern matching interface. The more common it becomes the more useful it will be to users. Especially if this were to be adopted into places like Rack, Net::HTTP, JSON, and other areas where frequently more imperative deconstructions and querying are already commonly used. I bring this up, rather than opening PRs, as I would like to see whether or not the core Ruby team is interested in these types of PRs and work of finding where else these interfaces may make sense. If you would like my more complete thoughts on this, and considerations for pattern matching interfaces in Ruby, I had written [Pattern Matching Interfaces in Ruby](https://docs.google.com/document/d/1spnuQTKy5i7Lx-sDCsORKN981Iam1IuNdOsYkhh9Yi0/edit#) some time ago to note concerns, potential guidelines, and other considerations. -- https://bugs.ruby-lang.org/ Unsubscribe: