Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> you want a type error, not a return of False, when you check equality against a completely unrelated type

What? No I don't?

    foo = if x: Foo else 1
    foo == 1 # why would I want this to throw? I just want False!
Disclaimer that I haven't done much Python recently (or Java, ever – I wonder if raise-on-== is a java-ism)


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.


>two things that aren't the same type can never be equal

Well, actually as long as they implement the right dundermethods, they can totally be.


Besides, there is a difference between the actual type of the objects `a` and `b` in `a == b` and the structural type you specify in an annotation.

For instance:

  from typing import Protocol


  class MyClass:
      def method_a(self) -> None:
          ...

      def method_b(self) -> None:
          ...


  class ProtocolA(Protocol):
      def method_a(self) -> None:
          ...


  class ProtocolB(Protocol):
      def method_b(self) -> None:
          ...


  def compare(a: ProtocolA, b: ProtocolB):
      if a == b:
          print("a == b")
      else:
          print("a != b")


  a = MyClass()
  b = MyClass()

  compare(a, b)

Clearly, you would expect the output to be "a == b".


> two things that aren't the same type can never be equal

You're saying is that an 8-bit number can never be equal to a 16-bit number, or a 32-bit number, or a...

I hope you agree that's ridiculous.


I hope you agree that's ridiculous.

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...


> No comparison without explicit casts is a valid design choice.

Sure, but that wasn't the claim.


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, ...)


> I wonder if raise-on-== is a java-ism

There are two forms of equality in Java:

- '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.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: