The Comparable variant tries to find a __eq__ with signature fn(T, T) -> Bool, which SIMD doesn’t have. The eqf variant calls the __eq__ defined on SIMD itself.
SIMD isn’t directly comparable because __eq__ and __ne__ return a SIMD[bool, ...] vector, not a single boolean.
We can’t make Scalar[dt] comparable (yet) because there’s no support for conditional conformance.
We also can’t simply change the Comparable trait to return a Boolable, since that introduces a confluence issue: for example, if bool(v) is defined as v.all(), then we would have the following:
(not not v) != v
since not v should translate to (~v).any() and not (~v).all().
I had a long discussion with @Joe about this last year, and it’s a rather tricky problem to resolve.
Ah, thank you for explaining this. I don’t know the ramifications, but I’d be in favor of renaming the simd-returning comparisons to make it possible for these types to conform to the comparable/equatable traits. I’m not sure the best way to spell this, but SIMD-returning comparisons are pretty rare, so I think that x.cmp_lt(y) or is_less etc would probably be acceptable.
Is there a good, non-surprising behavior for SIMD(1,2,3) < SIMD(1,3,2)? I would personally expect an element-wise comparison, and numpy also treats arrays this way.
I was also part of that discussion with Joe and Sora, and what we decided was that there was no good consistent behavior for any comparison other than a vector of length one. Equality should be doable for equal size vectors, since we can make that strict equality, but we are still left with how to handle size mismatches.
Conditional conformance should help handle the scalar case, but I don’t know if there’s a good behavior for width >= 2.
IIRC, Joe told me it’s exactly @adakkak who doesn’t like this idea. And for the record, I didn’t like it either.
We simply don’t handle size mismatches; they don’t type-check. That’s not the tricky part. The real contention lies between wanting syntactic sugar for numerical code and supporting generic programming.
That’s not how I remember it. We lack an Eq SIMD instance due to language/type system/API design constraints, not semantic ones. Moreover, being a value type (or a “regular” type, in the Stepanov sense) doesn’t inherently require additional comparison operators, so the absence of e.g. a < operator is far less problematic.
We can imagine a version of Mojo where dunder methods (which provide syntactic convenience) and Eq-conforming methods are distinct. In that hypothetical world, we could already implement Eq SIMD.
Of course, this approach comes with its own set of drawbacks, and Python doesn’t allow us to take this route. I’m merely using this example to illustrate how vast the design space is and how many ways there are to solve this problem (long post pending – stay tuned). My favourite is the Swift approach: introduce .op variants (e.g., .==) for element-wise comparison.