`<=` does not desugar correctly with custom struct

So I have a bit of code:

struct Foo
  val::Float64
end
@show 0.0 <= Foo(0.0)

but then MethodError: no method matching isless(::Float64, ::Foo). Oh right, I forgot to define isless:

struct Foo
  val::Float64
end
function Base.isless(lhs::Float64, rhs::Foo)
  Base.isless(lhs, rhs.val)
end
@show 0.0 <= Foo(0.0) # this prints false!

Wait what… how is this possible? But 0.0 <= 0.0!

Does anyone have any idea what’s going on here? This strikes me as a bug.

The documentation for <= says

Less-than-or-equals comparison operator. Falls back to (x < y) | (x == y).

In your case, x < y returns false as expected, but x == y also returns false because as the documentation says

Generic equality operator. Falls back to ===. Should be implemented for all types with a notion of equality, based on the abstract value that an instance represents.
[…]
New numeric types should implement this function for two arguments of the new type, and handle comparison to other types via promotion rules where possible.

So you need to implement ==, otherwise the default === is used which returns false in this case.

2 Likes

Thanks so much! This solves my problem, but I’d just like to note that the overall experience defining these operators is still quite confusing, esp. understanding which ones are required in order to get reasonable behavior.

In my case, I had tried defining isequal but not ==. I was (mistakenly) under the impression that these were equivalent.

1 Like

You’re welcome! Each of these operators/functions has an “Implementation” section in the documentation that says what should be implemented in which case, but I agree it’s a bit confusing.

Btw, why (x < y) | (x == y) and not (x < y) || (x == y)? Seems like there’s no harm in short-circuiting?

1 Like