9

In Swift 3, this is a compile error, if I use > or <

let a: Int?
guard a > 0 else {return}
guard a < 0 else {return}

Compile error:

Value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?'?


But it's okay if I compare with == or !=

let a: Int?
guard a == 0 else {return}
guard a != 0 else {return}
jscs
  • 63,694
  • 13
  • 151
  • 195
Vahid
  • 3,352
  • 2
  • 34
  • 42
  • 7
    What would you expect `nil < 0` to evaluate to? How about `-1 < nil`? It's not clear how these cases should be treated. With `nil == 0`, it's perfectly clear what it means. – Hamish Jun 28 '17 at 16:56
  • 1
    Compare https://stackoverflow.com/q/39251005/2976878 – Hamish Jun 28 '17 at 16:59
  • 1
    Proposal: [Remove Optional Comparison Operators](https://github.com/apple/swift-evolution/blob/master/proposals/0121-remove-optional-comparison-operators.md). Also, this has to be a dupe. – JAL Jun 28 '17 at 17:03
  • Also related: https://stackoverflow.com/questions/39427650/swift3-optionals-chaining-in-if-conditions-bug. – Martin R Jun 28 '17 at 17:17

3 Answers3

11

It makes perfect sense for the equality operator to support optionals, because it's absolutely clear that for any integer valued variable i:

  • nil == nil
  • nil != i
  • i != nil
  • i == i if and only if their values are the same

On the other hand, it's not clear how comparison to nil should act:

Is i less than nil?

  • If I want to sort an array so that all the nils come out at the end, then I would want i to be less than nil.
  • But if I want to sort an array so that all the nils come out at the start, then I would want i to be greater than nil.

Since either of these are equally valid, it wouldn't make sense for the standard library to favor one over the other. It's left to the programmer to implement whichever comparison makes sense for their use-case.

Here's a toy implementation that generates a comparison operator to suite either case:

func nilComparator<T: Comparable>(nilIsLess: Bool) -> (T?, T?) -> Bool {
    return {
        switch ($0, $1) {
            case (nil, nil): return false
            case (nil, _?): return nilIsLess
            case (_?, nil): return !nilIsLess
            case let (a?, b?): return a < b
        }
    }
}

let input = (0...10).enumerated().map {
    $0.offset.isMultiple(of: 2) ? Optional($0.element) : nil
}

func concisePrint<T>(_ optionals: [T?]) -> String {
    return "[" + optionals.map { $0.map{ "\($0)?" } ?? "nil" }.joined(separator: ", ") + "]"
}

print("Input:", concisePrint(input))
print("nil is less:", concisePrint(input.sorted(by: nilComparator(nilIsLess: true))))
print("nil is more:", concisePrint(input.sorted(by: nilComparator(nilIsLess: false))))

Output:

Input: [0?, nil, 2?, nil, 4?, nil, 6?, nil, 8?, nil, 10?]

nil is less: [nil, nil, nil, nil, nil, 0?, 2?, 4?, 6?, 8?, 10?]

nil is more: [0?, 2?, 4?, 6?, 8?, 10?, nil, nil, nil, nil, nil]

Alexander
  • 59,041
  • 12
  • 98
  • 151
2

Optional equality works logically, comparison doesn't.

  • 5 == 5 = true
  • 5 == nil = false
  • 5 == 6 = false
  • nil == nil = true

Those all make sense, but these don't:

  • 6 > 5 = true
  • 5 > 5 = false
  • 5 > nil = ??
  • nil > 5 = ??

This type of comparison does not have a simple answer, nor will that answer be the same depending on the use case.

GetSwifty
  • 7,568
  • 1
  • 29
  • 46
0

This is because Int and Int? are 2 different things.

According to the documentation, Int has overloads for < and > and some other operators, while Optional only has overloads for == and !=, see the documentation on Optional, the section talking about Comparing Optional Values.

Mo Abdul-Hameed
  • 6,030
  • 2
  • 23
  • 36