# Double-optional nil comparisons seem non-intuitive

**URL:** <https://forums.swift.org/t/double-optional-nil-comparisons-seem-non-intuitive/25620>\
**Category:** Using Swift\
**Tags:** optional, equality\
**Created:** [June 11, 2019, 6:52pm UTC](https://forums.swift.org/t/double-optional-nil-comparisons-seem-non-intuitive/25620 "2019-06-11T18:52:34Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![jpeckner](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jpeckner/32/7795_2.png) [@jpeckner](https://forums.swift.org/u/jpeckner)\
**Post date:** [June 11, 2019, 6:52pm UTC](https://forums.swift.org/t/double-optional-nil-comparisons-seem-non-intuitive/25620/1 "2019-06-11T18:52:34Z")

</div>

This result in this example makes perfect sense:

```
var shouldReturnNil: Bool? = nil
let doubleOptionalValue: String?? = shouldReturnNil.map { $0 ? nil : "MyValue" }
doubleOptionalValue // nil
doubleOptionalValue == nil // true 👍

```

But when the value of `doubleOptionalValue` is instead a _nested_ `nil`, comparing it to `nil`- which seems completely intuitive- returns `false`:

```
var shouldReturnNil: Bool? = true
let doubleOptionalValue: String?? = shouldReturnNil.map { $0 ? nil : "MyValue" }
doubleOptionalValue // Optional(nil)
doubleOptionalValue == nil // false 🤯 🤯 🤯

```

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:

```
doubleOptionalValue == Optional<Optional<String>>(Optional<String>(nil)) // true

```

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](https://github.com/apple/swift-evolution/blob/master/proposals/0230-flatten-optional-try.md), 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](https://forums.swift.org/t/optional-safe-subscripting-for-arrays/1100/23)).

I'd love to hear thoughts on this- please let me know if I'm misunderstanding how these comparisons are supposed to work!

(All issues found using Swift 5.0, Xcode 10.2.1)

---

<div class="post-metadata">

**Author:** ![gwendal.roue](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/gwendal.roue/32/13992_2.png) [@gwendal.roue](https://forums.swift.org/u/gwendal.roue)\
**Post date:** [June 11, 2019, 7:51pm UTC](https://forums.swift.org/t/double-optional-nil-comparisons-seem-non-intuitive/25620/2 "2019-06-11T19:51:24Z")

</div>

You are not wrong comparing the situation with the try? flattening. Double optionals are uneasy to deal with.

You may prefer using `flatMap` instead of `map`: you'll get good old regular optionals instead:

```swift
var shouldReturnNil: Bool? = nil
let doubleOptionalValue: String? = shouldReturnNil.flatMap { $0 ? nil : "MyValue" }
doubleOptionalValue // nil
doubleOptionalValue == nil // true 👍

var shouldReturnNil: Bool? = true
let doubleOptionalValue: String? = shouldReturnNil.flatMap { $0 ? nil : "MyValue" }
doubleOptionalValue // Optional(nil)
doubleOptionalValue == nil // true 👍

```

---

<div class="post-metadata">

**Author:** ![ben-cohen](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ben-cohen/32/296_2.png) [@ben-cohen](https://forums.swift.org/u/ben-cohen)\
**Post date:** [June 11, 2019, 8:07pm UTC](https://forums.swift.org/t/double-optional-nil-comparisons-seem-non-intuitive/25620/3 "2019-06-11T20:07:58Z")

</div>

Distinguishing between `nil` and "some value, which happens to be `nil`", is critical and not something that can be collapsed down. Take this example:

```swift
let maybeInts: [Int?] = [1,2,nil,3]
let i: Int? = nil
// returns .some(nil)
let firstNil = maybeInts.first(where: { $0 == i })
let j = 5
// returns nil
let firstFive = maybeInts.first(where: { $0 == j })

```

If `== nil` drilled into the inner `nil`, how would you distinguish between successfully finding a `nil` entry, and finding no result?

---

<div class="post-metadata">

**Author:** ![jpeckner](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jpeckner/32/7795_2.png) [@jpeckner](https://forums.swift.org/u/jpeckner)\
**Post date:** [June 11, 2019, 8:47pm UTC](https://forums.swift.org/t/double-optional-nil-comparisons-seem-non-intuitive/25620/4 "2019-06-11T20:47:47Z")

</div>

Nice, that's a simple and elegant solution. I've always defaulted to using `map`, but now this is making me think I should default to `flatMap` instead.

---

<div class="post-metadata">

**Author:** ![jpeckner](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jpeckner/32/7795_2.png) [@jpeckner](https://forums.swift.org/u/jpeckner)\
**Post date:** [June 11, 2019, 9:22pm UTC](https://forums.swift.org/t/double-optional-nil-comparisons-seem-non-intuitive/25620/5 "2019-06-11T21:22:15Z")

</div>

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

```

---

<div class="post-metadata">

**Author:** ![Jens](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jens/32/12903_2.png) [@Jens](https://forums.swift.org/u/Jens)\
**Post date:** [June 12, 2019, 4:45am UTC](https://forums.swift.org/t/double-optional-nil-comparisons-seem-non-intuitive/25620/6 "2019-06-12T04:45:05Z")

</div>

Remember that `nil` by itself has no type, just like eg `.none`, so their type has to be inferred.

The current behavior makes sense:

```swift
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? : )

> [@jpeckner](#):
>
> ```swift
> let doubleOptionalValue: String?? = nil
> doubleOptionalValue == nil // Shows warning that you might get a false negative
> doubleOptionalValue == Optional<Optional<Int>>(nil) // Doesn't show warning
> 
> ```

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.

---

<div class="post-metadata">

**Author:** ![jpeckner](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jpeckner/32/7795_2.png) [@jpeckner](https://forums.swift.org/u/jpeckner)\
**Post date:** [June 12, 2019, 6:22am UTC](https://forums.swift.org/t/double-optional-nil-comparisons-seem-non-intuitive/25620/7 "2019-06-12T06:22:47Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![kansaichris](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/kansaichris/32/2703_2.png) [@kansaichris](https://forums.swift.org/u/kansaichris)\
**Post date:** [June 12, 2019, 9:25am UTC](https://forums.swift.org/t/double-optional-nil-comparisons-seem-non-intuitive/25620/8 "2019-06-12T09:25:33Z")

</div>

Speaking of nested optionals, here's some more grist to the mill…

> [@Challenge: Finding base type of nested optionals](https://forums.swift.org/t/challenge-finding-base-type-of-nested-optionals/25096):
>
> Following on from my [last challenge](https://forums.swift.org/t/challenge-flattening-nested-optionals/24083), this one is somewhat trickier: Given an arbitrarily-nested Optional, the goal is to programmatically identify the inner-most Wrapped type (aka. its base type). Note that the optional may be nil at any depth, and it may be wrapped in further layers of Any and/or Optional. For example, an Int??? has a base type of Int, regardless of its value. Moreover, casting an Optional to Any, and/or wrapping it in additional layers of Optional, does not change its base t…
