[ruby-core:109653] [Ruby master Bug#18937] Inconsistent definitions of Complex#<=> and Numeric#<=> with others
From:
"msnm (Masahiro Nomoto)" <noreply@...>
Date:
2022-08-23 23:00:01 UTC
List:
ruby-core #109653
Issue #18937 has been updated by msnm (Masahiro Nomoto).
nobu (Nobuyoshi Nakada) wrote in #note-4:
> I agree that `Complex#<=>` should coerce the argument as well as other methods/classes.
> https://github.com/ruby/ruby/pull/6269
Thanks @nobu .
But `#coerce` is not called when a custom numeric class has `def real? = false` following `Complex#real?` .
```ruby
puts RUBY_DESCRIPTION
# ruby 3.2.0dev (2022-08-22T03:26:43Z :detached: d5f50463c2) [x86_64-linux]
class MyInteger < Numeric
def initialize(n) = (@n = n)
def coerce(other)
puts "MyInteger#coerce is called."
[other, @n]
end
end
class MyComplex < Numeric
def initialize(n) = (@n = Complex(n))
def real? = false
def coerce(other)
puts "MyComplex#coerce is called."
[other, @n]
end
end
p Complex(1) <=> MyInteger.new(2)
# MyInteger#coerce is called.
#=> -1
p Complex(1) <=> MyComplex.new(2)
#=> nil
# FYI: Complex#+ works fine.
p Complex(1) + MyComplex.new(2)
# MyComplex#coerce is called.
#=> (3+0i)
```
----------------------------------------
Bug #18937: Inconsistent definitions of Complex#<=> and Numeric#<=> with others
https://bugs.ruby-lang.org/issues/18937#change-98876
* Author: msnm (Masahiro Nomoto)
* Status: Closed
* Priority: Normal
* ruby -v: 3.1.2
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN
----------------------------------------
Object#<=> says "Returns 0 if `obj` and `other` are the same object or `obj == other`" .
https://ruby-doc.org/core-3.1.2/Object.html#method-i-3C-3D-3E
However, neither Complex#<=> nor Numeric#<=> satisfies that definition.
```ruby
num1 = Complex(0, 42)
num2 = Complex(0, 42)
p num1.equal?(num2) #=> false
p num1 == num2 #=> true
# using Complex#<=>
p num1 <=> num2 #=> nil
# using Numeric#<=>
Complex.remove_method(:<=>)
p num1 <=> num2 #=> nil
# using Object#<=> (Kernel#<=>)
Numeric.remove_method(:<=>)
p num1 <=> num2 #=> 0
```
Complex#<=> has another problem that it does not coerce numeric objects while Integer#<=> and Float#<=> do.
This prevents users from adding yet another complex class having #<=>.
---
Here is my proposal of Complex#<=> behavior (in Ruby).
This considers #15857, complex numbers are comparable when their imaginary parts are 0.
```ruby
class Complex
def <=>(other)
return (self == other ? 0 : nil) if self.imag != 0
if other.kind_of?(Complex)
if other.imag == 0
return self.real <=> other.real
else
return nil
end
elsif other.kind_of?(Numeric) && other.real?
return self.real <=> other
elsif other.respond_to?(:coerce)
num1, num2 = other.coerce(self)
return num1 <=> num2
else
return nil
end
end
end
```
--
https://bugs.ruby-lang.org/
Unsubscribe: <mailto:ruby-core-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>