Question

I'd like my class to be complete for several anticipated scenarios, including object comparison. My class is Hash-like in that it has a method to_hash, on which many of my class-completing methods are based. For example:

class Hashlike
  def == other
    if other.respond_to?(:to_hash)
      to_hash == other.to_hash
    end
  end
end

It seems overkill to override Hash#== just to inject similarity into the message's superchain:

class Hash
  def == other
    # Flip the comparison
    other.is_a?(Hashlike) ? other == self : super
  end
end

This will end up in stacktraces everywhere for entirely irrelevant issues. This feels invasive.

Is there a preferred or less invasive way to make Hash aware of my Hashlike's eligibility for comparison? And in general, is there a convention for extending core objects (Hash, Array) to receive core interface messages (==, nil?, <==>) and be aware of custom classes without leaving fingerprints all over their superchains?

Update:

The only optimization that occurs to me is to make the override call super first in the hopes of not choking a potentially heavily-used method with early conditional branching:

class Hash
  def == other
    # Lazily delay the comparison
    super or (other == self if other.is_a?(Hashlike))
  end
end

Some careful benchmarking (to the order of 1 billion comparisons) suggests that using either approach performs just as well as ruby's default of reversing the comparison if to_hash is defined on the received object, as mentioned in the answer below.

Was it helpful?

Solution

Hash already does this. If an object responds to to_hash, Hash#== should do other == self to allow for this sort of thing.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top