Feature #9123

Make Numeric#nonzero? behavior consistent with Numeric#zero?

Added by Erik Michaels-Ober 5 months ago. Updated 4 days ago.

[ruby-core:58404]
Status:Open
Priority:Normal
Assignee:Yukihiro Matsumoto
Category:core
Target version:-

Description

Numeric#zero? returns true or false, while Numeric#nonzero? returns self or nil.

I've written a patch that fixes this inconsistency and adds a Numeric#nonzero (non-predicate) method that returns self or nil for chaining comparisons. I'd like for this to be included in Ruby 2.1.0.

https://github.com/ruby/ruby/pull/452.patch

History

#1 Updated by Xavier Noria 5 months ago

Both predicates return a boolean value, whose exact nature is irrelevant. I see no inconsistency to fix.

In my view Ruby programmers have to internalize that all objects have a boolean interpretation.

#2 Updated by Erik Michaels-Ober 5 months ago

How would you feel if Numeric#zero? returned self (0) or nil?

Your description of the semantics of the question mark allows for this but I think it would be confusing. Likewise, Numeric#zero? returning truthy or falsey values as opposed to strict true or false is confusing.

Every method in Ruby returns a value that is either truthy or falsey so, by that logic, every method should end in a question mark.

#3 Updated by Xavier Noria 5 months ago

I wouldn't care. I use predicates as predicates

do_foo if x.zero?

also

do_bar if str =~ /.../

the =~ operator does not return singletons, but the return value of a predicate generally speaking is irrelevant. Only its semantics as a boolean value matter to me as a user of the predicate.

In my view, the singletons true and false are objects the programmer that writes the predicate has at his disposal in case nothing else does the job. In C you may return 0 or 1, in Ruby you have the singletons if nothing else at hand captures the semantics of the predicate.

#4 Updated by Bertram Scharpf 5 months ago

=begin

You did not only change the source code, but you also
removed an application example I admire for its beauty:

a = %w(z Bb bB bb BB a aA Aa AA A)
b = a.sort {|a,b| (a.downcase <=> b.downcase).nonzero? || a <=> b }
b   #=> ["A", "a", "AA", "Aa", "aA", "BB", "Bb", "bB", "bb", "z"]

I wonder how you manage not to notice anything about the impertinence of
your proposal.

=end

#5 Updated by Bertram Scharpf 5 months ago

I know I'm boring, but it is still my opinion that there should be a String#notempty? corresponding to Numeric#nonzero?.

#6 Updated by Erik Michaels-Ober 5 months ago

I did not remove that example, I just moved it under the documentation for Numeric#nonzero.

#7 Updated by Anonymous 5 months ago

Whereas the current implementation "works" as a predicate, I see no harm in
gradually polishing Ruby towards a better design. If one was to design that
from scratch, I bet there'd be no discussion -- predicates would return
either true or false.

I would love to see more of these kind of patches getting accepted.

--
Txus
(I'll be slowly moving off this address -- please contact me at
me@txus.ioif possible from now on.)

On Tue, Nov 19, 2013 at 2:32 AM, sferik (Erik Michaels-Ober) <
sferik@gmail.com> wrote:

Issue #9123 has been updated by sferik (Erik Michaels-Ober).

I did not remove that example, I just moved it under the documentation for

Numeric#nonzero.

Feature #9123: Make Numeric#nonzero? behavior consistent with Numeric#zero?
https://bugs.ruby-lang.org/issues/9123#change-43012

Author: sferik (Erik Michaels-Ober)
Status: Open
Priority: Normal
Assignee:
Category:
Target version:

Numeric#zero? returns true or false, while Numeric#nonzero? returns self
or nil.

I've written a patch that fixes this inconsistency and adds a
Numeric#nonzero (non-predicate) method that returns self or nil for
chaining comparisons. I'd like for this to be included in Ruby 2.1.0.

https://github.com/ruby/ruby/pull/452.patch

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

#8 Updated by Alexey Muranov 5 months ago

sferik (Erik Michaels-Ober) wrote:

How would you feel if Numeric#zero? returned self (0) or nil?

Your description of the semantics of the question mark allows for this but I think it would be confusing. Likewise, Numeric#zero? returning truthy or falsey values as opposed to strict true or false is confusing.

I like the argument that question-mark methods should return true/false (or maybe in some cases true/false/nil).

I think i am +1 for separate Numeric#nonzero? and Numeric#nonzero for readability.

Edited

#9 Updated by Xavier Noria 5 months ago

This ticket is not about changing the semantics of the Ruby language. It is a ticket about a particular predicate.

Changing the semantics of Ruby is an epic goal (and one that is unrealistic in my view, although legit to wish). But unless there is an agenda that says that is the future of Ruby, we have to judge this particular proposal according to the existing semantics of Ruby. With the current semantics of Ruby where the boolean space is flat, true is as true as 1, and there is not even a Boolean type (because the space is flat), in my opinion there is nothing to change in this predicate, it is perfectly fine.

Txus: look at all languages, old and modern. While some have the semantics you'd like (legit), like Java, the majority of them don't. I think that should tell you that language designers would not generally agree with what you take for granted is a "better design". It is a better design in your opinion, and if you designed a language you'd do it that way, perfect, but the data shows you'd likely loose your bet.

#10 Updated by Erik Michaels-Ober 5 months ago

=begin
I'm not proposing a change to the semantics of Ruby; I'm proposing a fix to an inconsistency.

The vast majority of predicate methods in Ruby return (({true})) or (({false})). There are approximately 200 such methods in the core library. By my count, there are only 6 core methods that return a truthy object or (({nil})):

  • (({Numeric#nonzero?}))
  • (({Module#autoload?}))
  • (({File::world_readable?}))
  • (({File::world_writable?}))
  • (({File::size?}))
  • (({Encoding::compatible?}))

In my opinion, (({Numeric#nonzero?})) is the most egregious aberration because (({Numeric#zero?})) returns (({true})) or (({false})). We could argue about whether it makes sense to change the other irregular methods but I believe this step toward consistency is a step in the right direction.
=end

#11 Updated by Bertram Scharpf 5 months ago

I'm not proposing a change to the semantics of Ruby; I'm proposing
a fix to an inconsistency.

The opposite of "zero?" is not "nonzero?" but "notzero?". If the method's
name was "notzero?", one could call it an inconsistency. "nonzero?" is
not a yes-no-question.

And yes, you are proposing an inconsistency, because a lot of useful
programs would no longer work.

#12 Updated by Erik Michaels-Ober 5 months ago

=begin

The opposite of "zero?" is not "nonzero?" but "notzero?". If the method's name was "notzero?", one could call it an inconsistency. "nonzero?" is not a yes-no-question.

Nonzero means "not equal to zero". It means the exact same thing as "not zero". It most certainly is a yes-no question. How else would you answer the question (({5.nonzero?}))?

And yes, you are proposing an inconsistency, because a lot of useful programs would no longer work.

I believe you're confusing the word "inconsistency" with "incompatibility". Is English not your native language or are you trolling?
=end

#13 Updated by Alexey Muranov 5 months ago

BertramScharpf (Bertram Scharpf) wrote:

The opposite of "zero?" is not "nonzero?" but "notzero?". If the method's
name was "notzero?", one could call it an inconsistency. "nonzero?" is
not a yes-no-question.

"Nonzero" is an adjective, opposite of "zero" used as an adjective. "Not zero" is the negation of "zero", so essentially the same thing as "nonzero", but not a single word.

#14 Updated by Yusuke Endoh 5 months ago

I don't think it is possible to change the spec.
Many programs in the wild actually use the behavior.

https://github.com/search?q=nonzero%3F+sort+extension%3Arb&type=Code

By the way, I investigated the early history of nonzero?.

In the beginning, Nagai proposed a "<>" operator that returns -1, nil, 1
in ruby-list:7286. The motivation example was:

def structcmp (a, b)
(a.mem
a <> a.mema) || (a.memb <=> a.mem_b)
end

Obviously he had in mind Perl's common idiom for lexicographic sort:

sort { a[0] <=> b[0] || a[1] <=> b[1] }

because it is 1998 :-)

Matz said he was negative to this proposal in because
it would make the language complex to add a new operator, and because
he thought that it would be better to change the boolean semantics,
that is, to handle 0 as false (!).

Nagai, in , disagreed with the semantic change because of
a philosophical reason (it looked weird to him to handle only one instance
of Integer as false), and reclaimed adding a new operator.

In , Funaba disagreed with a new operator because the
lexicographic sort is not a common operation, he thought.
He also disagreed with the semantic change because of a compatibility
issue (in spite of 1998).
And then, nonzero? appeared in the counter-proposal he made:

(a <=> b).nonzero? || (a2 <=> b2)

Matz liked and implemented the proposal in a day .

Because nonzero? was accepted, a "<>" operator became less significant.
Nagai, in , dismissed his proposal.

In the mail, he pointed the inconsistency that is now discussed in this
ticket. He asked why matz implemented not only nonzero? but also zero?.
Matz's answer was:

  • symmetry of zero? and nonzero?
  • lisp/scheme provides zero?

(According to this history, it makes no sense to fit nonzero? with zero?.)

Incidentally, the discussion continued, and Keiju proposed Array#<=> in
, that is,

[a1, a2] <=> [b1, b2]

After the further discussion, this was accepted too.

Funaba, who proposed nonzero? himself, preferred Array#<=> to nonzero?,
but also pointed out that Array#<=> is not a perfect substitute to
nonzero? because Array#<=> does not shortcut.

After that, nonzero? exists until now.

Yusuke Endoh mame@tsg.ne.jp

#15 Updated by Benoit Daloze 5 months ago

mame (Yusuke Endoh) wrote:

By the way, I investigated the early history of nonzero?.

Very interesting, thank you a lot for reporting it!

Personally I do sometimes use #nonzero? semantics when I want an Integer, but another value if it happens to be 0.
I think it complements the fact any Integer is truthy in Ruby.

#16 Updated by Andrew Vit 5 months ago

The history on this is interesting. I would agree with the consistency idea of nonzero = (nil | 1) and nonzero? = (false | true) but I don't know if this could be changed now.

Also, why should we return nil in any case? I think the correct return value should be false, not nil. (nil implies an unknown or unavailable answer, but we do know if the number is 0 or not.) In other words:

42.nonzero? #=> 42
0.nonzero?  #=> false

#17 Updated by Alexey Muranov 5 months ago

avit (Andrew Vit) wrote:

Also, why should we return nil in any case? I think the correct return value should be false, not nil. (nil implies an unknown or unavailable answer, but we do know if the number is 0 or not.) In other words:

42.nonzero? #=> 42
0.nonzero?  #=> false

IMO, if 0.nonzero? is false, then 42.nonzero? should be true.

Slightly off topic, but how about something general like

class Object
  def non(*args)
    self unless args.include? self
  end
end

#18 Updated by Yusuke Endoh 5 months ago

avit (Andrew Vit) wrote:

42.nonzero? #=> 42
0.nonzero?  #=> false

In fact, the first version of nonzero? returned self or false.

Inaba, in ruby-dev:6417, suggested a convention about the usage
of nil and false.

  • if a method returns only a boolean, it should use true and false
  • if a method returns a general object but also "false" value, it should use nil

Matz accepted it, and adopted the convention to another exception, defined?.
As far as I know, this is the only spec change of nonzero? ever.

Inaba pointed out that a method whose name ends with "?" would make a user feel
that it should return only a boolean, though he also admitted that it is difficult
to change because of compatibility issue (in 1999).
Matz agreed, and said that "?" is a symbol that is "mainly used as a predicate".

Yusuke Endoh mame@tsg.ne.jp

#19 Updated by Fuad Saud 5 months ago

I don't understand why we would want to be so strict about types on this
case. Having some arbitrary value being returned is useful and it doesn't
hurt any good practices. Ensuring predicates return true or false feels
useless for me

On Friday, November 22, 2013, mame (Yusuke Endoh) wrote:

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

avit (Andrew Vit) wrote:

42.nonzero? #=> 42
0.nonzero?  #=> false

In fact, the first version of nonzero? returned self or false.

Inaba, in ruby-dev:6417, suggested a convention about the
usage
of nil and false.

  • if a method returns only a boolean, it should use true and false
  • if a method returns a general object but also "false" value, it should use nil

Matz accepted it, and adopted the convention to another exception,
defined?.
As far as I know, this is the only spec change of nonzero? ever.

Inaba pointed out that a method whose name ends with "?" would make a user
feel
that it should return only a boolean, though he also admitted that it is
difficult
to change because of compatibility issue (in 1999).
Matz agreed, and said that "?" is a symbol that is "mainly used as a
predicate".

Yusuke Endoh >

Feature #9123: Make Numeric#nonzero? behavior consistent with Numeric#zero?
https://bugs.ruby-lang.org/issues/9123#change-43094

Author: sferik (Erik Michaels-Ober)
Status: Open
Priority: Normal
Assignee:
Category:
Target version:

Numeric#zero? returns true or false, while Numeric#nonzero? returns self
or nil.

I've written a patch that fixes this inconsistency and adds a
Numeric#nonzero (non-predicate) method that returns self or nil for
chaining comparisons. I'd like for this to be included in Ruby 2.1.0.

https://github.com/ruby/ruby/pull/452.patch

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

--
Fuad Saud

twitter http://twitter.com/fuadsaud |
linkedinhttp://www.linkedin.com/in/fuadksd|
coderwall http://coderwal.com/fuadsaud | githubhttp://github.com/fuadsaud|

#20 Updated by Alexey Muranov 5 months ago

fuadksd (Fuad Saud) wrote:

I don't understand why we would want to be so strict about types on this
case. Having some arbitrary value being returned is useful and it doesn't
hurt any good practices. Ensuring predicates return true or false feels
useless for me

In my opinion, it is because code is intended mostly for reading, not for writing.

a.sort {|a,b| (a.downcase <=> b.downcase).nonzero? || a <=> b }

is unexpected or confusing.

#21 Updated by Bertram Scharpf 5 months ago

alexeymuranov (Alexey Muranov) wrote:

In my opinion, it is because code is intended mostly for reading, not for writing.

a.sort {|a,b| (a.downcase <=> b.downcase).nonzero? || a <=> b }

is unexpected or confusing.

That's a matter of taste. In my eyes, this code example is beautiful and clear.
It could be more efficient using #casecmp instead of two #downcases, what you
clearly can see if it is one line.

#22 Updated by Alexey Muranov 5 months ago

BertramScharpf (Bertram Scharpf) wrote:

That's a matter of taste. In my eyes, this code example is beautiful and clear.
It could be more efficient using #casecmp instead of two #downcases, what you
clearly can see if it is one line.

I do not understand what is a matter of taste here. Everybody seems to agree that the only problem is the compatibility. How would the following be less beautiful or clear?

a.sort {|a,b| (a.downcase <=> b.downcase).nonzero || a <=> b }

or

a.sort {|a,b| (a.downcase <=> b.downcase).non(0) || a <=> b }

#23 Updated by Fuad Saud 5 months ago

nonzero? returning the number is useful; I used this recently:

t '.itemscount', count: itemscount.nonzero? || t(.'no')

It returns the number or, if it's zero, the proper translation for no (generating a "no items" message).

I feel like cases like this one are more common than we imagine.

#24 Updated by Rodrigo Rosenfeld Rosas 5 months ago

Now I finally understand the purpose of nonzero? :) In several languages, including Perl and JavaScript, 0 is a falsy value, but this is not the case for Ruby. So it's kind of a hack to make 0 be treated as a falsy value :) Indeed, now that I understand it, I could find quite some usage for it :)

#25 Updated by Marc-Andre Lafortune 5 months ago

  • Category set to core
  • Assignee set to Yukihiro Matsumoto

There is no use-case for this request and it would cause many incompatibilities (e.g. in rake, thor, ruby itself, ...):

https://github.com/jimweirich/rake/blob/bfafc3a0/lib/rake/application.rb#L300
https://github.com/ruby/ruby/blob/8612344834d4/lib/test/unit.rb#L398
https://github.com/ruby/ruby/blob/8612344834d4/lib/test/unit.rb#L398

The only "problem" is that some of you dislike the fact that a method ending in '?' returns something else than true or false. This is similar to String < Hash returning nil. Embrace Ruby, love it for its clever quirks. Or else I'd suggest writing your own new language instead of trying to "reinvent" Ruby.

I'll let Matz reject this request. In the meantime, I'm accepting wagers from anyone that thinks there's a chance that this will ever be accepted.

#26 Updated by Rodrigo Rosenfeld Rosas 5 months ago

Just for the record, I only said I can see how this behavior can be useful in cases you want to consider 0 (zero) as falsey. But I do actually prefer that boolean methods (those ending with a question mark) do always returned strict boolean values. I've even considered a while ago to create a new feature request to ask Ruby to always convert non-strict boolean values to either true or false, but I never actually created the issue because I was pretty sure it wouldn't be accepted.

The reason for that is that if Ruby main goal is to make programmers happy, and since I'm a programmer, I'd be much happier if I could know for sure if a value of a "method?" call is always true or false. Of course I'm not the only programmer out there ;) But what if you want to debug some code by using the print technique:

p "debugging something?", something?, other_values

I'd expect to read either true or false after "debugging something?", but instead I could get several lines as the output of "something?.to_s" if it returns an object instead of true.

Even if we consider only the boolean semanthic for Ruby and stop thinking about true and false, I still don't like the name of the method. Why would a method ending in a question mark, which is supposed to return either a truthy or falsey value, actually return a consistent value instead of only worrying about returning either truthy or falsey? I mean, such methods are meant to be used as "if obj.nonzero?" as opposed to "obj.nonzero? || anything" in the sense that the value returned by nonzero? is actually meaningful as a non-truthy value. I think I'm not able to make sense of my words, but what I mean is that I'd prefer that something like "nonzero?" was actually called "nonzero" (without the question mark) or even a better name than that.

"nonzero" only means that it's not zero. It doesn't give any hint it will return a meaningful number in case it's non zero. Reading code using nonzero? to return an expected number in case it's non zero would definitely make me pretty unhappy.

I don't actually expect this feature request to be accepted, but I'd just like to take the chance to state my opinions on the subject.

#27 Updated by Eamonn Webster 5 months ago

Ever asked someone 'Do you know the time?' and they answer 'Yes'. So you ask 'What time is it?' and mutter 'Jerk!' under your breath.
Ruby is a friendly language, let's not turn it into a jerk.

#28 Updated by Rodrigo Rosenfeld Rosas 5 months ago

Humans don't have to ask if a number is not a zero :)

#29 Updated by Alexey Muranov 5 months ago

eweb (Eamonn Webster) wrote:

Ever asked someone 'Do you know the time?' and they answer 'Yes'. So you ask 'What time is it?' and mutter 'Jerk!' under your breath.
Ruby is a friendly language, let's not turn it into a jerk.

Between #know_time? and #time_now!, i would prefer the second to get the actual time. :)

#30 Updated by Alexey Muranov 5 months ago

Please correct me if i am wrong, but i think that the most useful part of this proposal is deprecating Numeric#nonzero? with its current behavior in favour of Numeric#nonzero.

#31 Updated by Bertram Scharpf 5 months ago

alexeymuranov (Alexey Muranov) wrote:

Please correct me if i am wrong, but i think that the most useful
part of this proposal is [...].

There is no useful part in this proposal. Its only effect is an
endless discussion between people, who embrace Ruby and love it for
its clever quirks, and people who begrudge them their innocent joy.

#32 Updated by Andrew Vit 5 months ago

i think that the most useful part of this proposal is deprecating Numeric#nonzero? with its current behavior in favour of Numeric#nonzero.

Personally I would agree with this too. I guess I'm not a fan of quirks if they're inconsistent...

  • Asking "are you nonzero?" should be true/false (eventually).
  • Saying "give me your nonzero value" should be the number, or nil.

Sorry, I know it's just a bikeshed argument...

#33 Updated by Guilherme Schneider 4 months ago

I think current implementation of Numeric#nonzero? is fine, because, as was said, a predicate is expected to return truthy or falsey values.

On the other hand, Numeric#nonzero? should only be used as a predicate. So one should not rely on it returning self if is not zero. But, because so many do use nonzero? expecting that it returns self if it is not zero (and indeed the spec say so), changing it would break compatibility.

So, what about keeping current implementation of Numeric#nonzero? and adding Numeric#nonzero (with identical implementation), but intended to be used in non predicate cases, like

a.sort {|a,b| (a.downcase <=> b.downcase).nonzero || a <=> b }

And then maybe deprecate usage of Numeric#nonzero? in non predicate cases (and changing the spec saying that nonzero? returns true if the number is not zero).

#34 Updated by Xavier Noria 4 months ago

@Guilherme

A predicate in Ruby can return any object. If the exact return value is documented, then the user of the predicate can leverage that contract if so wishes. That's why it is documented.

If something is documented to return the singletons true/false, then you can rely on that and pass them to a JSON generator directly, if self is documented there is nothing wrong in exploting that fact. The =~ operator documents index or nil, fine, use the index if you want. Rails generally documents the equivalent of Object, that is, in most cases it says nothing about the exact value. There your only choice is to use it for its boolean semantics.

#35 Updated by Charles Nutter 4 days ago

Might as well toss in my opinion...

I don't care as a normal Ruby user whether a method returns truthy/falsey or true/false, because I shouldn't care about what the object is if all I need to know is its truthiness.

However, I don't like that the truthy value returned by some methods is self, because it's exposing more information than the method needs to expose.

This case is tricky, since the self object being returned is only a bit more information than the boolean value. The only real argument to change this is consistency. If you only care about the truthy value, you probably don't care about this issue. If you care about the self value, you don't want it to change. And it is documented to provide the current behavior.

I'd vote to change this, but I don't have a strong opinion. I will say this needs to be treated as a visible, potentially-breaking API change, which needs a major release. I'm not sure what ruby-core considers a major release at this point (x.0.0 or x.y.0).

Also available in: Atom PDF