Skip to content

Instantly share code, notes, and snippets.

@schmidt
Created May 30, 2022 08:33
Show Gist options
  • Save schmidt/e846087810033cac62fcd2bfd3c31ace to your computer and use it in GitHub Desktop.
Save schmidt/e846087810033cac62fcd2bfd3c31ace to your computer and use it in GitHub Desktop.
Easier inheritance when implementing `==`
class Point
attr_reader :x
attr_reader :y
def initialize(x:, y:)
@x = x
@y = y
end
# def ==(other)
# self.class == other.class &&
# @x == other.x &&
# @y == other.y
# end
def ==(other)
other.is_a?(Point) &&
x == other.x &&
y == other.y
end
end
class PolarPoint < Point
attr_reader :angle, :radius
def initialize(angle:, radius:)
@angle = angle
@radius = radius
end
def x
radius * Math.cos(angle)
end
def y
radius * Math.sin(angle)
end
end
p1 = Point.new(x: 1, y: 0)
p2 = PolarPoint.new(angle: 0, radius: 1)
# Reflexivity
p1 == p1 # => true
p2 == p2 # => true
# Symmetry
p1 == p2 # => true
p2 == p1 # => true
@schmidt
Copy link
Author

schmidt commented May 30, 2022

When using

  def ==(other)
    self.class == other.class &&
      @x == other.x &&
      @y == other.y
  end

to implement == there's no way for PolarPoint to work as a drop-in replacement for Point. Unless PolarPoint performs some serious meta-programming magic and overwrites #class, it can never be equal to a Point. By using is_a? we're giving the related / derived class more power. Futhermore, by using other.is_a?(Point) instead of other.is_a?(self.class) we can make sure that "Symmetry" works out of the box.

If we're using other.is_a?(self.class) in the base implementation in Point#== as suggested in my tweet, we lose symmetry. Thenp1 == p2 but p2 != p1. But at least we can fix that by implementing an adjusted PolarPoint#== to re-establish symmetry. That's something that would not be possible when comparing #class.

All of this all is only relevant when Point is part of a library/gem. If it's all your application code, you can of course easily adjust the implementation of Point#== when you introduce PolarPoint.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment