From: "Dan0042 (Daniel DeLorme)" Date: 2022-08-16T17:53:36+00:00 Subject: [ruby-core:109506] [Ruby master Feature#17330] Object#non Issue #17330 has been updated by Dan0042 (Daniel DeLorme). mame (Yusuke Endoh) wrote in #note-15: > ``` > params[:name]&.non(&:empty?) > ``` > > Frankly speaking, this above is dead hard to read and write for me. Do you all really want this? Personally speaking, yes, the above is clear to me, and reads easily, much more than the alternatives. > ``` > return Faraday.get(url).non { |r| r.code >= 400 }&.body > ``` On the other hand I also find this hard to read. But it's all up to the programmer to write easy-to-read code. This is one of the tools that could help. Or hinder depending on how it's used. "The **possibility** to make code cryptic itself should not be the reason to withdraw a feature." [-Matz](https://bugs.ruby-lang.org/issues/15723#note-2) ---------------------------------------- Feature #17330: Object#non https://bugs.ruby-lang.org/issues/17330#change-98675 * Author: zverok (Victor Shepelev) * Status: Open * Priority: Normal ---------------------------------------- (As always "with core" method proposals, I don't expect quick success, but hope for a fruitful discussion) ### Reasons: Ruby always tried to be very chainability-friendly. Recently, with introduction of `.then` and `=>`, even more so. But one pattern that frequently emerges and doesn't have good idiomatic expression: calculate something, and if it is not a "good" value, return `nil` (or provide default value with `||`). There are currently two partial solutions: 1. `nonzero?` in Ruby core (frequently mocked for "inadequate" behavior, as it is looking like predicate method, but instead of `true`/`false` returns an original value or `nil`) 2. ActiveSupport `Object#presence`, which also returns an original value or `nil` if it is not "present" (e.g. `nil` or `empty?` in AS-speak) Both of them prove themselves quite useful in some domains, but they are targeting only those particular domains, look unlike each other, and can be confusing. ### Proposal: Method `Object#non` (or `Kernel#non`), which receives a block, calls it with receiver and returns `nil` (if block matched) or receiver otherwise. ##### Prototype implementation: ```ruby class Object def non self unless yield(self) end end ``` ##### Usage examples: 1. With number: ```ruby limit = calculate.some.limit limit.zero? ? DEFAULT_LIMIT : limit # or, with nonzero? calculate.some.limit.nonzero? || DEFAULT_LIMIT # with non: calculate.some.limit.non(&:zero?) || DEFAULT_LIMIT # ^ Note here, how, unlike `nonzero?`, we see predicate-y ?, but it is INSIDE the `non()` and less confusing ``` 2. With string: ```ruby name = params[:name] if params[:name] && !params[:name].empty? # or, with ActiveSupport: name = params[:name].presence # with non: name = params[:name]&.non(&:empty?) ``` 3. More complicated example ```ruby action = payload.dig('action', 'type') return if PROHIBITED_ACTIONS.include?(action) send("do_#{action}") # with non & then: payload.dig('action', 'type') .non { |action| PROHIBITED_ACTIONS.include?(action) } &.then { |action| send("do_#{action}") } ``` Basically, the proposal is a "chainable guard clause" that allows to "chain"ify and DRYify code like: ```ruby value = fetch_something return value unless value.with_problems? # which turns into fetch_something.non(&:with_problems?) # or value = fetch_something value = reasonable_default if value.with_problems? # turns into value = fetch_something.non(&:with_problems?) || reasonable_default ``` I believe that this idiom is frequent enough, in combinations like (assorted examples) "read config file but return `nil` if it is empty/wrong version", "fetch latest invoice, but ignore if it has an `unpayable` flag", "fetch a list of last user's searches, but if it is empty, provide default search hints" etc. I believe there _is_ un unreflected need for idiom like this, the need that is demonstrated by the existence of `nonzero?` and `presence`. -- https://bugs.ruby-lang.org/ Unsubscribe: