ruby-core@ruby-lang.org archive (unofficial mirror)
 help / color / mirror / Atom feed
* [ruby-core:100897] [Ruby master Feature#17330] Object#non
@ 2020-11-17 12:49 zverok.offline
  2020-11-17 12:55 ` [ruby-core:100898] " nobu
                   ` (19 more replies)
  0 siblings, 20 replies; 21+ messages in thread
From: zverok.offline @ 2020-11-17 12:49 UTC (permalink / raw)
  To: ruby-core

Issue #17330 has been reported by zverok (Victor Shepelev).

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

* 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:

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:

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

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/

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [ruby-core:100898] [Ruby master Feature#17330] Object#non
  2020-11-17 12:49 [ruby-core:100897] [Ruby master Feature#17330] Object#non zverok.offline
@ 2020-11-17 12:55 ` nobu
  2021-01-02  9:46 ` [ruby-core:101876] " sawadatsuyoshi
                   ` (18 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: nobu @ 2020-11-17 12:55 UTC (permalink / raw)
  To: ruby-core

Issue #17330 has been updated by nobu (Nobuyoshi Nakada).


It reminded me https://bugs.ruby-lang.org/issues/12075#change-57152.

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

* 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:

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:

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

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/

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [ruby-core:101876] [Ruby master Feature#17330] Object#non
  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 ` sawadatsuyoshi
  2021-01-12  5:40 ` [ruby-core:102017] " akr
                   ` (17 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: sawadatsuyoshi @ 2021-01-02  9:46 UTC (permalink / raw)
  To: ruby-core

Issue #17330 has been updated by sawa (Tsuyoshi Sawada).


I think the proposed feature would be useful, but I feel that your focus on use cases with a negative predicate is artificial. Positive predicates should have as many corresponding use cases. And since negation is always one step more complex than its positive counterpart, you should first (or simultaneously) propose the positive version, say `Object#oui`:

```ruby
calculate.some.limit.oui(&:nonzero?) || DEFAULT_LIMIT

params[:name]&.oui{ _1.empty?.! }

payload.dig('action', 'type').oui{ PROHIBITED_ACTIONS.include?(_1).! }
```

Furthermore, I suggest you may also propose the method(s) to take a variable numbers of arguments to match against:

```ruby
payload.dig('action', 'type').non(*PROHIBITED_ACTIONS)
```

And by "match", I think that using the predicate `===` would be more useful than `==`:


```ruby
"foo".non(/\AError: /, /\AOops, /) # => "foo"
"Oops, something went wrong.".non(/\AError: /, /\AOops, /) # => nil
```



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

* 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/

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [ruby-core:102017] [Ruby master Feature#17330] Object#non
  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
  2021-01-13  7:07 ` [ruby-core:102054] " matz
                   ` (16 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: akr @ 2021-01-12  5:40 UTC (permalink / raw)
  To: ruby-core

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/

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [ruby-core:102054] [Ruby master Feature#17330] Object#non
  2020-11-17 12:49 [ruby-core:100897] [Ruby master Feature#17330] Object#non zverok.offline
                   ` (2 preceding siblings ...)
  2021-01-12  5:40 ` [ruby-core:102017] " akr
@ 2021-01-13  7:07 ` matz
  2021-01-23 16:41 ` [ruby-core:102222] " zverok.offline
                   ` (15 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: matz @ 2021-01-13  7:07 UTC (permalink / raw)
  To: ruby-core

Issue #17330 has been updated by matz (Yukihiro Matsumoto).


I don't see the `non` method make code more readable by glancing at the examples.
Can you elaborate on the benefit of the proposal?

Matz.

 

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

* 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/

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [ruby-core:102222] [Ruby master Feature#17330] Object#non
  2020-11-17 12:49 [ruby-core:100897] [Ruby master Feature#17330] Object#non zverok.offline
                   ` (3 preceding siblings ...)
  2021-01-13  7:07 ` [ruby-core:102054] " matz
@ 2021-01-23 16:41 ` zverok.offline
  2021-01-24  2:42 ` [ruby-core:102223] " nobu
                   ` (14 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: zverok.offline @ 2021-01-23 16:41 UTC (permalink / raw)
  To: ruby-core

Issue #17330 has been updated by zverok (Victor Shepelev).


@matz Thinking a bit more about it, what I frequently lack is a "singular reject": "drop this value if it doesn't match expectations".

```ruby
# HTTP-get something, don't return result unless successful:
response = Faraday.get(url)
return response unless response.code >= 400
# or worse:
unless response.code >= 400
  return response.body
end

# With non
return Faraday.get(url).non { |r| r.code >= 400 }
# Note how little changes necessary to say "successful response's body"
return Faraday.get(url).non { |r| r.code >= 400 }&.body
```
Though, it seems for me when explaining that one needs a symmetric pair of "singular select"/"singular reject", because particular Faraday examples maybe better written as

```ruby
return Faraday.get(url).when(&:successful?)
# Note how little changes necessary to say "successful response's body"
return Faraday.get(url).when(&:successful?)&.body
```


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

* 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/

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [ruby-core:102223] [Ruby master Feature#17330] Object#non
  2020-11-17 12:49 [ruby-core:100897] [Ruby master Feature#17330] Object#non zverok.offline
                   ` (4 preceding siblings ...)
  2021-01-23 16:41 ` [ruby-core:102222] " zverok.offline
@ 2021-01-24  2:42 ` nobu
  2021-01-24  9:31 ` [ruby-core:102225] " zverok.offline
                   ` (13 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: nobu @ 2021-01-24  2:42 UTC (permalink / raw)
  To: ruby-core

Issue #17330 has been updated by nobu (Nobuyoshi Nakada).


zverok (Victor Shepelev) wrote in #note-8:
> ```ruby
> return Faraday.get(url).when(&:successful?)
> # Note how little changes necessary to say "successful response's body"
> return Faraday.get(url).when(&:successful?)&.body
> ```

This seems readable enough and more flexible.

```ruby
return Faraday.get(url).then {_1.body if _1.successful?}
```

Or, make `successful?` to return `self` or `nil`, then
```ruby
return Faraday.get(url).successful?&.body
```


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

* 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/

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [ruby-core:102225] [Ruby master Feature#17330] Object#non
  2020-11-17 12:49 [ruby-core:100897] [Ruby master Feature#17330] Object#non zverok.offline
                   ` (5 preceding siblings ...)
  2021-01-24  2:42 ` [ruby-core:102223] " nobu
@ 2021-01-24  9:31 ` zverok.offline
  2021-01-25 15:54 ` [ruby-core:102236] " daniel
                   ` (12 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: zverok.offline @ 2021-01-24  9:31 UTC (permalink / raw)
  To: ruby-core

Issue #17330 has been updated by zverok (Victor Shepelev).


@nobu

> This seems readable enough and more flexible.

```ruby
return Faraday.get(url).then {_1.body if _1.successful?}
```

I argue that the idiom (return something/continue with something, conditionally) is so frequent, that it should be more atomic. 

> Or, make `successful?` to return `self` or `nil`

That's what I started this ticket with!
* This is not natural for Ruby: `nonzero?` is frequently mocked and generally considered a "design error"; Rails' `presence` has kind of the same vibe
* This requires me to change Faraday (which, to be clear, I have no relation to), and then go and change every library which causes the same idiom :)
* This is solving very particular case (just like `nonzero?` and `presence`), I am talking about generic.

Here's one more example:
```ruby
Faraday.get(url).body.then { JSON.parse(_1, symbolize_names: true) }.non { _1.key?('error') }&.dig('user', 'status')
```

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

* 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/

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [ruby-core:102236] [Ruby master Feature#17330] Object#non
  2020-11-17 12:49 [ruby-core:100897] [Ruby master Feature#17330] Object#non zverok.offline
                   ` (6 preceding siblings ...)
  2021-01-24  9:31 ` [ruby-core:102225] " zverok.offline
@ 2021-01-25 15:54 ` daniel
  2022-08-08  7:51 ` [ruby-core:109446] " byroot (Jean Boussier)
                   ` (11 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: daniel @ 2021-01-25 15:54 UTC (permalink / raw)
  To: ruby-core

Issue #17330 has been updated by Dan0042 (Daniel DeLorme).


FWIW, +1 from me

At first I thought the only uses were `non(&:zero?)` and `non(&:empty?)` which, while I find very elegant, are not enough to justify adding a method to Kernel. But I think zverok has shown enough other uses to make a convincing case.

There might be something to say about how this provides an automatic complement to any predicate method:
```ruby
if user.subscription.non(&:expired?)     # is more grammatically correct (English-wise)
if !user.subscription.expired?           # than the traditional negation
if user.subscription.expired?.!          # or akr style

if user.subscription&.non(&:expired?)    # particularly useful in chain
if s = user.subscription and !s.expired?           # the alternatives
if user.subscription&.then{ |s| s if !s.expired? } # are not very
if user.subscription&.then{ _1 if !_1.expired? }   # attractive (IMO)
```

Overall this "singular reject/select" goes in the same direction as `tap` and `then`, so if those two are considered improvements to ruby, I think `non` and `when` would be the same kind of improvement.

Can we also consider `non(:empty?)` in the same vein as `inject`, or is that an anti-pattern?

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

* 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/

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [ruby-core:109446] [Ruby master Feature#17330] Object#non
  2020-11-17 12:49 [ruby-core:100897] [Ruby master Feature#17330] Object#non zverok.offline
                   ` (7 preceding siblings ...)
  2021-01-25 15:54 ` [ruby-core:102236] " daniel
@ 2022-08-08  7:51 ` byroot (Jean Boussier)
  2022-08-10 17:48 ` [ruby-core:109467] " Dan0042 (Daniel DeLorme)
                   ` (10 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: byroot (Jean Boussier) @ 2022-08-08  7:51 UTC (permalink / raw)
  To: ruby-core

Issue #17330 has been updated by byroot (Jean Boussier).


I also think `not` would be a better name for it:

```ruby
class Object
  def not
    self unless yield self
  end
end
```

Something that comes up frequently if to check wether an argument is either nil or empty (e.g. https://twitter.com/_ko1/status/1555424953193070593), and safe navigation can't be used because there is no inverse of `empty?`:

```ruby
def do_something(arg)
  if arg && !arg.empty?
    # ... snip
  end
end
```

Which often lead users to incorrectly use `any?`:

```ruby
  if arg&.any?
    # ... snip
  end
```

With this proposal it could be written as:

```ruby
  if arg&.not(&:empty?)
    # ... snip
  end
```

Which isn't much shorter, but flows much better IMHO.



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

* 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/

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [ruby-core:109467] [Ruby master Feature#17330] Object#non
  2020-11-17 12:49 [ruby-core:100897] [Ruby master Feature#17330] Object#non zverok.offline
                   ` (8 preceding siblings ...)
  2022-08-08  7:51 ` [ruby-core:109446] " byroot (Jean Boussier)
@ 2022-08-10 17:48 ` Dan0042 (Daniel DeLorme)
  2022-08-16 15:02 ` [ruby-core:109497] " mame (Yusuke Endoh)
                   ` (9 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Dan0042 (Daniel DeLorme) @ 2022-08-10 17:48 UTC (permalink / raw)
  To: ruby-core

Issue #17330 has been updated by Dan0042 (Daniel DeLorme).


byroot (Jean Boussier) wrote in #note-13:
> I also think `not` would be a better name for it

Since there is already a `not` operator, I would expect a `not` method to have the same semantics and return either true or false. So since we're talking about a method that returns `self` I prefer "non".
`42.nonzero? == 42.non(&:zero?)`

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

* 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/

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [ruby-core:109497] [Ruby master Feature#17330] Object#non
  2020-11-17 12:49 [ruby-core:100897] [Ruby master Feature#17330] Object#non zverok.offline
                   ` (9 preceding siblings ...)
  2022-08-10 17:48 ` [ruby-core:109467] " Dan0042 (Daniel DeLorme)
@ 2022-08-16 15:02 ` mame (Yusuke Endoh)
  2022-08-16 15:23 ` [ruby-core:109501] " zverok (Victor Shepelev)
                   ` (8 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: mame (Yusuke Endoh) @ 2022-08-16 15:02 UTC (permalink / raw)
  To: ruby-core

Issue #17330 has been updated by mame (Yusuke Endoh).


```
params[:name]&.non(&:empty?)
```

Frankly speaking, this above is dead hard to read and write for me. Do you all really want this?

```
return Faraday.get(url).non { |r| r.code >= 400 }&.body
```

This is confusing to me whether to return a body when `r.code >= 400` or when `r.code < 400`.

```
r = Faraday.get(url)
return r.code < 400 ? r.body : nil
```

I believe this plain old style is much clearer.

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

* 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/

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [ruby-core:109501] [Ruby master Feature#17330] Object#non
  2020-11-17 12:49 [ruby-core:100897] [Ruby master Feature#17330] Object#non zverok.offline
                   ` (10 preceding siblings ...)
  2022-08-16 15:02 ` [ruby-core:109497] " mame (Yusuke Endoh)
@ 2022-08-16 15:23 ` zverok (Victor Shepelev)
  2022-08-16 16:19 ` [ruby-core:109504] " mame (Yusuke Endoh)
                   ` (7 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: zverok (Victor Shepelev) @ 2022-08-16 15:23 UTC (permalink / raw)
  To: ruby-core

Issue #17330 has been updated by zverok (Victor Shepelev).


@mame One thing that is really frequent in Rails codebases is code like this:
```ruby
params.presence || DEFAUT_PARAMS
# or, quoting from above, what I write as...
params[:name]&.non(&:empty?)
# in Rails would be...
params[:name].presence
```
(not only `params`-related, it is just an example)

It relies on Rails-y (and PHP-y, but not Ruby-ish) concept of "every empty object is not _present_". Its existence and popularity is notable. But it is 
a) very particular (only empty/falsy objects), and at the same time
b) very imprecise (empty AND falsy objects, so by the `.presence` you can't tell whether `nil` could be here or not, or `false` could be etc.)

I am trying to generalize it to be suitable for the chainable style of computations. The existence of `nonzero?` highlights that Ruby core devs also tried to address the problem.

I find that frequently existence of "chainable-friendly" API inspires the design of other APIs, say, Faraday example can be simplified with 
```ruby
Faraday.get(url).non(&:error?)&.body
```
Which spells as a direct telling what's happening: "get response from URL, [then, it it is] non-error, get the response body"


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

* 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/

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [ruby-core:109504] [Ruby master Feature#17330] Object#non
  2020-11-17 12:49 [ruby-core:100897] [Ruby master Feature#17330] Object#non zverok.offline
                   ` (11 preceding siblings ...)
  2022-08-16 15:23 ` [ruby-core:109501] " zverok (Victor Shepelev)
@ 2022-08-16 16:19 ` mame (Yusuke Endoh)
  2022-08-16 17:53 ` [ruby-core:109506] " Dan0042 (Daniel DeLorme)
                   ` (6 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: mame (Yusuke Endoh) @ 2022-08-16 16:19 UTC (permalink / raw)
  To: ruby-core

Issue #17330 has been updated by mame (Yusuke Endoh).


I understand the logic, but the result of your generalization, `&.non(&:empty?)`, is hard to read anyway. @matz says it too in #note-7. The short idiom includes a branch (`&.`), negation (`non`), and implicit Symbol#to_proc hack. It is very brain-intensive, and reminiscent of Perl.

I guess the generalization is an impossible idea. I like the specific `container#nonempty?` in #12075 (except its name). If we were to introduce something even slightly generalized to this problem, `x.empty?.not` that @akr proposed in #note-6 would be a reasonable compromise, IMO.

> The existence of `nonzero?` highlights that Ruby core devs also tried to address the problem.

The birth of `nonzero?` is a bit more particular, and it is considered no longer needed in modern times. I wrote its history in https://bugs.ruby-lang.org/issues/9123#change-43083 . It is not so related to this topic, but you may be interested.

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

* 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/

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [ruby-core:109506] [Ruby master Feature#17330] Object#non
  2020-11-17 12:49 [ruby-core:100897] [Ruby master Feature#17330] Object#non zverok.offline
                   ` (12 preceding siblings ...)
  2022-08-16 16:19 ` [ruby-core:109504] " mame (Yusuke Endoh)
@ 2022-08-16 17:53 ` Dan0042 (Daniel DeLorme)
  2022-08-17  3:23 ` [ruby-core:109507] " ujihisa (Tatsuhiro Ujihisa)
                   ` (5 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Dan0042 (Daniel DeLorme) @ 2022-08-16 17:53 UTC (permalink / raw)
  To: ruby-core

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/

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [ruby-core:109507] [Ruby master Feature#17330] Object#non
  2020-11-17 12:49 [ruby-core:100897] [Ruby master Feature#17330] Object#non zverok.offline
                   ` (13 preceding siblings ...)
  2022-08-16 17:53 ` [ruby-core:109506] " Dan0042 (Daniel DeLorme)
@ 2022-08-17  3:23 ` ujihisa (Tatsuhiro Ujihisa)
  2022-08-17  4:46 ` [ruby-core:109508] " sawa (Tsuyoshi Sawada)
                   ` (4 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: ujihisa (Tatsuhiro Ujihisa) @ 2022-08-17  3:23 UTC (permalink / raw)
  To: ruby-core

Issue #17330 has been updated by ujihisa (Tatsuhiro Ujihisa).


> non() and oui()

Since what these 2 methods do aren't trivial from the names, how's the following 2-words names instead?

`itself_if`
`itself_unless`

These return either the object itself or nil, so it makes sense to have the method names what to return.
We are already familiar with the concept of itself/if/unless, so these names should be self descriptive too.

Also for example `non` is shorter than `nonzero?` but it doesn't seem to be used more widely than `nonzero?` in general.


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

* 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/

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [ruby-core:109508] [Ruby master Feature#17330] Object#non
  2020-11-17 12:49 [ruby-core:100897] [Ruby master Feature#17330] Object#non zverok.offline
                   ` (14 preceding siblings ...)
  2022-08-17  3:23 ` [ruby-core:109507] " ujihisa (Tatsuhiro Ujihisa)
@ 2022-08-17  4:46 ` sawa (Tsuyoshi Sawada)
  2022-08-17  5:13 ` [ruby-core:109509] " zverok (Victor Shepelev)
                   ` (3 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: sawa (Tsuyoshi Sawada) @ 2022-08-17  4:46 UTC (permalink / raw)
  To: ruby-core

Issue #17330 has been updated by sawa (Tsuyoshi Sawada).


ujihisa (Tatsuhiro Ujihisa) wrote in #note-19:
[H]ow's the following 2-words names instead?
> 
> `itself_if`
> `itself_unless`

I agree it is more descriptive and may be better. Or, perhaps, we can simply have `Object#if`, `Object#unless`.

```ruby
calculate.some.limit.if(&:nonzero?) || DEFAULT_LIMIT
params[:name]&.unless(&:empty?)
```

> I wonder if `itself_unless` is actually useful rather than confusing.
> You can easily rewrite `itself_unless` with `itself_if` with `!`, such as `x.itself_if { !_1.pred? }` which is pretty straightforward.

I agree.

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

* 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/

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [ruby-core:109509] [Ruby master Feature#17330] Object#non
  2020-11-17 12:49 [ruby-core:100897] [Ruby master Feature#17330] Object#non zverok.offline
                   ` (15 preceding siblings ...)
  2022-08-17  4:46 ` [ruby-core:109508] " sawa (Tsuyoshi Sawada)
@ 2022-08-17  5:13 ` zverok (Victor Shepelev)
  2022-08-17  5:36 ` [ruby-core:109510] " hmdne (hmdne -)
                   ` (2 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: zverok (Victor Shepelev) @ 2022-08-17  5:13 UTC (permalink / raw)
  To: ruby-core

Issue #17330 has been updated by zverok (Victor Shepelev).


TBH, I believe that core method names should be short, recognizable and elegant in context in the first place, not explaining what they do in multiple words.
We are thinking about "learn once, use 1000 times" here first, not only about a period of introduction of the new method.

It is the same as `yield_self`, which kinda "said what it did" but looked ugly and over-explaining for everyday use and was promptly renamed to `then`.
The same is related to the idea of `itself_if`/`itself_unless`, I believe (it is like using `each_returning_result` instead of `map`).

Just `#if` and `#unless` look tempting, though.

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

* 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/

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [ruby-core:109510] [Ruby master Feature#17330] Object#non
  2020-11-17 12:49 [ruby-core:100897] [Ruby master Feature#17330] Object#non zverok.offline
                   ` (16 preceding siblings ...)
  2022-08-17  5:13 ` [ruby-core:109509] " zverok (Victor Shepelev)
@ 2022-08-17  5:36 ` hmdne (hmdne -)
  2022-08-17 14:30 ` [ruby-core:109514] " Dan0042 (Daniel DeLorme)
  2022-08-19  5:09 ` [ruby-core:109565] " matz (Yukihiro Matsumoto)
  19 siblings, 0 replies; 21+ messages in thread
From: hmdne (hmdne -) @ 2022-08-17  5:36 UTC (permalink / raw)
  To: ruby-core

Issue #17330 has been updated by hmdne (hmdne -).


I had another idea regarding a #non method that I haven't voiced before, but may result in cleaner code overall:

```ruby
class Negator < ::BasicObject
  def initialize(obj)
    @object = obj
  end

  def method_missing(...)
    !@object.__send__(...) && @object
  end

  def respond_to_missing?(...)
    @object.respond_to?(...)
  end
end

class Object
  def non
    Negator.new(self)
  end
end

[].non.empty? # => false
[123].non.empty? # => [123]
```

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

* 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/

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [ruby-core:109514] [Ruby master Feature#17330] Object#non
  2020-11-17 12:49 [ruby-core:100897] [Ruby master Feature#17330] Object#non zverok.offline
                   ` (17 preceding siblings ...)
  2022-08-17  5:36 ` [ruby-core:109510] " hmdne (hmdne -)
@ 2022-08-17 14:30 ` Dan0042 (Daniel DeLorme)
  2022-08-19  5:09 ` [ruby-core:109565] " matz (Yukihiro Matsumoto)
  19 siblings, 0 replies; 21+ messages in thread
From: Dan0042 (Daniel DeLorme) @ 2022-08-17 14:30 UTC (permalink / raw)
  To: ruby-core

Issue #17330 has been updated by Dan0042 (Daniel DeLorme).


ujihisa (Tatsuhiro Ujihisa) wrote in #note-19:
> `itself_if`
> `itself_unless`

zverok (Victor Shepelev) wrote in #note-21:
> Just `#if` and `#unless` look tempting, though.

Interesting suggestion. Let's look at the previous "non" examples rewritten with "itself_unless" and "unless"

```ruby
calculate.some.limit.non(&:zero?) || DEFAULT_LIMIT
name = params[:name]&.non(&:empty?)
payload.dig('action', 'type').non { |action| PROHIBITED_ACTIONS.include?(action) }
fetch_something.non(&:with_problems?)
return Faraday.get(url).non { |r| r.code >= 400 }&.body
if user.subscription&.non(&:expired?) 

calculate.some.limit.itself_unless(&:zero?) || DEFAULT_LIMIT
name = params[:name]&.itself_unless(&:empty?)
payload.dig('action', 'type').itself_unless { |action| PROHIBITED_ACTIONS.include?(action) }
fetch_something.itself_unless(&:with_problems?)
return Faraday.get(url).itself_unless { |r| r.code >= 400 }&.body
if user.subscription&.itself_unless(&:expired?) 

calculate.some.limit.unless(&:zero?) || DEFAULT_LIMIT
name = params[:name]&.unless(&:empty?)
payload.dig('action', 'type').unless { |action| PROHIBITED_ACTIONS.include?(action) }
fetch_something.unless(&:with_problems?)
return Faraday.get(url).unless { |r| r.code >= 400 }&.body
if user.subscription&.unless(&:expired?) 
```

YMMV, but to my eye it looks like "non" is more expressive when used with a single predicate method, and "itself_unless" is more readable for longer blocks. And "unless" is remarkably readable in both situations.

> I wonder if `itself_unless` is actually useful rather than confusing.
> You can easily rewrite `itself_unless` with `itself_if` with `!`, such as `x.itself_if { !_1.pred? }` which is pretty straightforward.

I think it's more useful than `itself_if`. In many cases the negation itself is what makes this useful, otherwise you would just use the predicate directly.

```ruby
if user.subscription&.active?              # if subscription has "active?" there is no need here
if user.subscription&.itself_if(&:active?) # to use "oui" or "itself_if"

if user.subscription&.non(&:expired?)           # if subscription only has "expired?" it needs to be negated,
if user.subscription&.itself_if{ !_1.expired? } # which is why we need "non" or a negated "itself_if" or
if user.subscription&.itself_unless(&:expired?) # "itself_unless"
```

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

* 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/

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [ruby-core:109565] [Ruby master Feature#17330] Object#non
  2020-11-17 12:49 [ruby-core:100897] [Ruby master Feature#17330] Object#non zverok.offline
                   ` (18 preceding siblings ...)
  2022-08-17 14:30 ` [ruby-core:109514] " Dan0042 (Daniel DeLorme)
@ 2022-08-19  5:09 ` matz (Yukihiro Matsumoto)
  19 siblings, 0 replies; 21+ messages in thread
From: matz (Yukihiro Matsumoto) @ 2022-08-19  5:09 UTC (permalink / raw)
  To: ruby-core

Issue #17330 has been updated by matz (Yukihiro Matsumoto).


I still don't think `non` (or `itself_if` or `itself_unless`) gain readability. It may work well in some cases, but may be too easy to be abused.

Matz.


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

* 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/

^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2022-08-19  5:09 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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 ` [ruby-core:102017] " akr
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)

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).