What's special here is not the operator ==. What's special is the language rule that values are implicitly wrapped in an optional when passed to a function (or an operator) taking an optional parameter.
Here what happens is this:
u == 7
gets promoted to this:
u == Optional(7)
And now you're comparing two Int?. There is no implicit unwrapping of u but instead 7 is implicitly wrapped because == requires two similar types and == is implemented for two optional values of the same type.
The difference with operator < is that it isn't implemented for Optional where Wrapped: Comparable unlike == which is implemented for Optional where Wrapped: Equatable. So no implicit wrapping can allow the operator to be called.
By the way, this implicit wrapping in optional is why you can write:
Which is exactly what makes operator == special: compared to other operators that are left unimplemented == (and its != counterpart) is the one that is implemented. Am I missing something?
I suppose you could say Optional conforming to Equatable when the wrapped value is Equatable is something special. It's actually pretty common however: most collections have such a conformance for instance.
The original question asked why you don't need to unwrap first before calling ==, then proceeded to use other operators like < as an example of something working as expected (requiring to unwrap first). And the reason is because of something unique to optionals granted by the language: implicit wrapping of non-optional values. I see that as a special rule for Optional in the language, whereas the presence of (or not) of some operators is more a normal API usage/design question.
That discussion happened > 7 years ago, many things changes since then. Now we have mature generics and it it would be possible making a conditional conformance to Comparable. From the pitch:
Good time to have this discussion or everyone is happy with the current status quo?
ā¦what remains is to decide whether these semantics (that nil is "less than" any non-nil value) are actually useful and worth keeping.
I could not find a single use case in the proposal (which is about the removal of comparisons with optionals) which would show the usefulness of such comparisons (e.g. better use a compact map).
I think this would be a case of ātoo much magicā. Unless someone shows me a compelling example, please spare me such surprising code.
It might be a good idea to review this.
Update: Yes, I do not like implicitly initialized optionals. I even suspect this would be a breaking change thatās worth it.
I don't think it is very special, or unexpected, given that Optional<A> is Equatable whenever A: Equatable, combined with optional promotion. All functions and operator behave this way.
However, I did find it somewhat surprising that I could compare a non-equatable optional value against nil.
struct NonEquatable {}
let x: NonEquatable? = nil
if x == nil { // what?! A non-equatable?!
"nil"
} else {
"not nil"
}
However, I found that I could replicate this behavior with no special treatment once I realized that nil is in fact a literal, and it is possible to inspect the "optionalness" of an optional using pattern matching:
Well, "no special treatment" in the sense that there is no compiler magic at play, simply user code. Here, I cannot compare x against any arbitrary other NonEquatable? value, but I can compare it agains the literalnil.
One should not start to think in some theoretical/complicated way, but start with what an optional is supposed to be conceptually and what follows from that in a "natural" way.:
Checking if an optional is equal to nil is at the heart of what an optional should be.
Then when you have two optionals of something equatable:
1st optional
2nd optional
equal?
nil
nil
true
value 1
nil
false
nil
value 1
false
value 1
value 2
if and only if value 1 = value 2
This is the starting point and the intuition that most people will have about it, no matter what the compiler is doing in the background. The mechanics of the language should make sure that the intuition is met, and not the way around, so to speak. Else, you will have a hard time writing or understanding code.
Conversely, if you start introducing effects that do not fit well with this "clear" intuition, you should have strong reasons to do so.
Not quite, for me starting point is thinking of Optional as a type, then == nil is comparing to .none and == someValue is unwrapping it with Equatable (and whatever rule there for WrappedValue).
The actual code the standard library uses to enable comparisons of non-Equatable instances to nil is in stdlib/public/core/Optional.swift between lines 430 and 607, if you're interested.
Except for being named _OptionalNilComparisonType instead of _Nil and a few extra overloads and @transparent@frozen etc, the code is more or less the same as I wrote up top <3