ruby-core@ruby-lang.org archive (unofficial mirror)
 help / color / mirror / Atom feed
From: akr@fsij.org
To: ruby-core@ruby-lang.org
Subject: [ruby-core:102017] [Ruby master Feature#17330] Object#non
Date: Tue, 12 Jan 2021 05:40:04 +0000 (UTC)	[thread overview]
Message-ID: <redmine.journal-89865.20210112054004.710@ruby-lang.org> (raw)
In-Reply-To: redmine.issue-17330.20201117124902.710@ruby-lang.org

Issue #17330 has been updated by akr (Akira Tanaka).


I prefer "not" method than this "non" method.

`x.empty?.not`

Although `x.empty?.!` is possible now, `!` method is not very readable.

----------------------------------------
Feature #17330: Object#non
https://bugs.ruby-lang.org/issues/17330#change-89865

* 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}") }
    ```

### Possible extensions of the idea

It is quite tempting to define the symmetric method named -- as we already have `Object#then` -- `Object#when`:
```ruby
some.long.calculation.when { |val| val < 10 } # returns nil if value >= 10
# or even... with support for ===
some.long.calculation.when(..10)&.then { continue to do something }
```
...but I am afraid I've overstayed my welcome :)




-- 
https://bugs.ruby-lang.org/

  parent reply	other threads:[~2021-01-12  5:40 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-11-17 12:49 [ruby-core:100897] [Ruby master Feature#17330] Object#non zverok.offline
2020-11-17 12:55 ` [ruby-core:100898] " nobu
2021-01-02  9:46 ` [ruby-core:101876] " sawadatsuyoshi
2021-01-12  5:40 ` akr [this message]
2021-01-13  7:07 ` [ruby-core:102054] " matz
2021-01-23 16:41 ` [ruby-core:102222] " zverok.offline
2021-01-24  2:42 ` [ruby-core:102223] " nobu
2021-01-24  9:31 ` [ruby-core:102225] " zverok.offline
2021-01-25 15:54 ` [ruby-core:102236] " daniel
2022-08-08  7:51 ` [ruby-core:109446] " byroot (Jean Boussier)
2022-08-10 17:48 ` [ruby-core:109467] " Dan0042 (Daniel DeLorme)
2022-08-16 15:02 ` [ruby-core:109497] " mame (Yusuke Endoh)
2022-08-16 15:23 ` [ruby-core:109501] " zverok (Victor Shepelev)
2022-08-16 16:19 ` [ruby-core:109504] " mame (Yusuke Endoh)
2022-08-16 17:53 ` [ruby-core:109506] " Dan0042 (Daniel DeLorme)
2022-08-17  3:23 ` [ruby-core:109507] " ujihisa (Tatsuhiro Ujihisa)
2022-08-17  4:46 ` [ruby-core:109508] " sawa (Tsuyoshi Sawada)
2022-08-17  5:13 ` [ruby-core:109509] " zverok (Victor Shepelev)
2022-08-17  5:36 ` [ruby-core:109510] " hmdne (hmdne -)
2022-08-17 14:30 ` [ruby-core:109514] " Dan0042 (Daniel DeLorme)
2022-08-19  5:09 ` [ruby-core:109565] " matz (Yukihiro Matsumoto)

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.ruby-lang.org/en/community/mailing-lists/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=redmine.journal-89865.20210112054004.710@ruby-lang.org \
    --to=ruby-core@ruby-lang.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).