From: "Dan0042 (Daniel DeLorme) via ruby-core" Date: 2023-07-11T20:48:26+00:00 Subject: [ruby-core:114138] [Ruby master Feature#19764] Introduce defp keyword for defining overloadable, pattern matched methods Issue #19764 has been updated by Dan0042 (Daniel DeLorme). I think there's two ideas here that need to be considered separately. One is method overloading, and the other is pattern matching in the method signature. Method overloading has never existed in ruby. Why is not possible to have both `def foo(x)` and `def foo(x,y)`? I don't know if it just happened that way or if it was a conscious design decision by Matz. The idea of automatic dispatching without any boilerplate code is certainly appealing. But when needed I find it's not too much trouble to write a case statement with dispatch to different sub-methods. I have written code like this before, but not very often. Not often enough that I can say it's worth the extra complexity of building it into the language. On the other hand, having pattern matching in the method signature is something I would love to see. It can have so many uses. `def foo(nb => Integer)` class validation `def foo(minutes => 0..59)` range/structure validation `def foo(if: => condition)` alias for keyword argument (#18402) `def foo(v => @hostname)` easy way to set instance variables (#15192 and many others) ---------------------------------------- Feature #19764: Introduce defp keyword for defining overloadable, pattern matched methods https://bugs.ruby-lang.org/issues/19764#change-103817 * Author: zeke (Zeke Gabrielse) * Status: Open * Priority: Normal ---------------------------------------- Pattern matching has become one of my favorite features of Ruby, if not my favorite. It changed the way I write and express my thoughts through clean, maintainable code. And I'd like to use it *more*. I propose a new keyword, `defp`, for defining a method which applies pattern matching to its arguments. ```ruby defp call(String => s unless s in /^[a-z]/) puts "string: #{s.inspect} (capitalized)" end defp call(String => s) puts "string: #{s.inspect}" end defp call(Hash(foo:, bar:) => h) puts "hash: #{h.inspect}" end defp call(**nil) puts "no keyword args" end call("Example") # => string: "Example" (capitalized) call("test") # => string: "test" call(foo: 1, bar: 2) # => hash: { :foo => 1, :bar => 2 } ``` Internally, this could be represented as the following `case..in` pseudocode: ```ruby def call(...) case ... in String => s unless s in /foo/ puts "string: #{s.inspect} (not foo)" in String => s puts "string: #{s.inspect}" in Hash(foo:, bar:) => h puts "hash: #{h.inspect}" in **nil puts "no keyword args" else raise NoMatchingMethod end end ``` As you could imagine, this could be used to refactor a lot of code, making the developer's intent much clearer. From [complex methods that use `case` statements](https://github.com/rails/rails/blob/593893c901f87b4ed205751f72df41519b4d2da3/actionpack/lib/action_dispatch/routing/url_for.rb#L173-L193) for taking varied arguments (I'm sure all our code bases contain such `case` statements), to defining smaller, simpler methods that handle particular argument patterns. In addition, not only can this improve code quality, but it brings in method overloads, and it also adds a way to define more typing to the language -- something that RBS has tried to do, to mixed reactions -- but in a more Ruby-like way that Rubyists are already learning *and loving*. Thoughts? Original idea by Victor Shepelev: https://zverok.space/blog/2023-05-05-ruby-types.html Further discussion: https://news.ycombinator.com/item?id=35834351 -- https://bugs.ruby-lang.org/ ______________________________________________ ruby-core mailing list -- ruby-core@ml.ruby-lang.org To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/