From: "msnm (Masahiro Nomoto)" Date: 2022-08-23T23:00:01+00:00 Subject: [ruby-core:109653] [Ruby master Bug#18937] Inconsistent definitions of Complex#<=> and Numeric#<=> with others 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: