Yup, that's true. In Python, generally if you want to only compare equal to the same type, you would use the NotImplemented value to implement:
class MyClass:
def __eq__(self, other):
if not isinstance(other, MyClass):
return NotImplemented
else:
# whatever your comparison is
When NotImplemented is returned, there is a fallback sequence described in https://docs.python.org/3/library/numbers.html#implementing-... - where basically, in the expression `A == B`, if `A.__eq__(B)` returns NotImplemented, then `B.__eq__(A)` is checked as well (falling back to an `is` identity comparison if both return NotImplemented).
This is neat because it allows you to define a class B that can compare equal to an existing class A with the expression A == B, even though class A has no knowledge of B.
You don't want it to raise an exception at run-time, but you probably want it to be a static type error: two things that aren't the same type can never be equal, so if you're bothering to compare them you're at best wasting cycles and at worst making a dumb mistake.
In your example, `foo` could be marked (in mypy) a `Union[Foo, int]`, which would be a reasonable value to compare against an `int` and not a type error.
But there are times you want heterogenous types to compare with ==. Your approach isn't compatible with ad-hoc polymorphism.
For an example, consider `np.eye(4) == 1`. In this, you're comparing a doubly nested array of floats with a scalar int, but the operation is well defined (vectorized equality comparison).
And yes, it should be feasible to typecheck that operation.
Yep. This is what I meant. I don't want to throw a runtime error. I want the type checker to be able to actually type check this statically, which mypy can't do.
No comparison without explicit casts is a valid design choice. I think Ada and Rust work that way, though I've never used either language and might be mistaken...
Yeah, there was some interpretation of the intent behind the comment on my part (cf "you probably want it to be a static type error").
How you view the claim itself depends on your thoughts about the level at which equality should operate (should type tags be considered part of a value's identity, is it about the bit representation, or the abstract value encoded by that representation, ...)
- 'x == y' is an identity check, like 'x is y' in Python (are x and y referencing the same memory?). These must be of the same type, or else it won't compile.
- 'x.equals(y)' is an overridable method, like 'x == y' in Python. Its argument is of type 'Object', so it will accept anything.
I don't do much Java these days, but I do write a lot of Scala (which runs on the JVM). One of the first things I do in a Scala project is load the 'cats' package so I can use 'x.eqv(y)' which refuses to compile if x and y are of incompatible types. This problem also propagates up through different APIs, e.g. 'x.contains(y)' checks whether y appears in the collection x (which could be a list, hashmap, etc.); this doesn't check the types, and I've ended up with bugs that would have been caught if it were stricter. I now prefer to define a safer alternative 'x.has(y)' which uses 'x.exists(z => z.eqv(y))'.
The Python stdlib all seems to disagree with the GP and provide False for comparisons with values of different types. Personally, I didn't expect this, because Python tends to not like mixing types in any way, but it's the one place where it makes sense.
What? No I don't?
Disclaimer that I haven't done much Python recently (or Java, ever – I wonder if raise-on-== is a java-ism)