I do understand why this is happening; the nil expression in the last line is expanded to Optional<Optional>.none, which isn't what doubleOptionalValue equals at that point. This is really what I was looking for:
But in reality, all we really care about 95% of the time is whether the base value is nil, not an intermediary wrapper level. That was the intention behind flattening optional try? statements, and while that's not directly related to this, it feels like it's in the same vein. (And if I'm understanding his intentions correctly, it might also be counter to doing "something sensible" as Jordan Rose said in Optional safe subscripting for arrays - #23 by jrose).
I'd love to hear thoughts on this- please let me know if I'm misunderstanding how these comparisons are supposed to work!
That makes sense, thanks for spelling that out. I agree, there needs to be a way to distinguish nil values from each other depending on how nested they are.
Would it make sense for the compiler to warn against comparing multi-layer optionals to a nil literal? I.e:
let doubleOptionalValue: String?? = nil
doubleOptionalValue == nil // Shows warning that you might get a false negative
doubleOptionalValue == Optional<Optional<Int>>(nil) // Doesn't show warning
Remember that nil by itself has no type, just like eg .none, so their type has to be inferred.
The current behavior makes sense:
let nestedOptional: Optional<Optional<Int>> = nil
print( nestedOptional == nil ) // true (because type of nil is inferred to Int??)
print( nestedOptional == nil as Int? ) // false
print( nestedOptional == nil as Int?? ) // true
print( nestedOptional == nil as Int??? ) // false
print( nestedOptional == nil as Int???? ) // false
print( nestedOptional == nil as Int????? ) // false
print( nestedOptional == nil as Int?????? ) // false
print("First three written with less special syntax:")
print( nestedOptional == nil ) // true (because type of nil is inferred to Int??)
print( nestedOptional == Optional<Int>(nil) ) // false
print( nestedOptional == Optional<Optional<Int>>(nil) ) // true
print("First three written with no special syntax:")
print( nestedOptional == .none ) // true (because type of .none is inferred to Int??)
print( nestedOptional == Optional<Int>.none ) // false
print( nestedOptional == Optional<Optional<Int>>.none ) // true
Also, by the logic of your suggested warning, you would get a warning on the first line of your example too, right? : )
I think a lot of the confusion around (nested) optionals comes from all the special syntax/sugar that has been added in order to make them more convenient. I don't know if the conveniences could be more consistent in order to lessen the confusion.
Hehe, true, the first line would have a warning too! Which I wouldn't be opposed to. I honestly can't remember the last time I wanted to actually use a double-optional type (the bug which led me to create this post was due to an accidental double-optional). That's why I like @gwendal.roue's suggestion to use flatMap and basically avoid nested optionals altogether.