1

Optional values unlike nil / NULL cannot by default be compared with numerical values. What steps must be taken to have e.g. nil < 1 and nil < 1.0 evaluate to true?

Ilias Karim
  • 4,798
  • 3
  • 38
  • 60
  • 3
    `(optionalVar ?? 0) == 0`? – user28434'mstep Feb 11 '19 at 14:46
  • 1
    I think optional comparison with nil is remove in Swift 3: https://github.com/apple/swift-evolution/blob/master/proposals/0121-remove-optional-comparison-operators.md – Paul Peelen Feb 11 '19 at 14:46
  • 3
    @PaulPeelen: You can still test optionals for *equality* (if the underlying type allows it) – only `<` and friends have been removed for optionals. Of course `nil` does never compare equal to a non-nil value. – Martin R Feb 11 '19 at 15:10
  • ... From SE-0121: *Variants of == and != which accept optional operands are still useful, and their results unsurprising, so they will remain.* – Martin R Feb 11 '19 at 15:16
  • 1
    Such a comparison doesn't really make sense. It's not too far off from saying "What steps can I take to make `"Bob" < "Alice"`?" The answer isn't to edit the `String` type, it's to make a better comparator. For comparing optionals, I [made this.](https://stackoverflow.com/a/44808567/3141234) – Alexander Feb 11 '19 at 15:51

2 Answers2

1

nil == 0 will never evaluate to true because it is false. Similarly 1 == 0 will not evaluate to true, and there are no reasonable steps you can take to make it do so. Unequal things should not return true for ==.

The syntax you want is (nil ?? 0) == 0, which is true.

But if you find yourself trying to treat nil as 0 very often (often enough that you would want some special handling), then that suggests your type is incorrect. Why is this variable Optional? It sounds like this variable should be just an Int in the first place. If it comes into your system as Optional (most commonly via JSON), you should convert it to a non-Optional type as early as you can and use that throughout the rest of the system. The vast majority of variables and properties in Swift should not be Optional. If they are (and many people make this mistake), you will find yourself fighting the system constantly.


To your edit, Swift used to allow you to use < with nil this way. It was removed on purpose (SE-0121) because it causes so many really subtle bugs (just like treating NULL implicitly as 0 has long caused bugs in C and ObjC). You definitely should not try to put that back in. It's a really bad semantic.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • You’re correct, Rob. I appreciate your very well reasoned response. My hope is that there is an extension one could write for Optional e.g. conforming to Comparable or Numeric to not only prevent an engineer from having to write `(nil ?? 0)` (which admittedly is not that onerous) but also make Swift more C-like like it’s predecessor, Objective-C. – Ilias Karim Feb 11 '19 at 15:28
  • 1
    You shouldn't expect to make Swift more C like in this way. Swift's type system is in direct response to the many problems with C's implicit and ambiguous conversions. They removed this on purpose because it has caused a lot of bugs in C and ObjC. – Rob Napier Feb 11 '19 at 15:45
  • 1
    @IliasKarim I think you're probably making a mistake with treating nil as 0. There are very few situations in which that really makes sense. If a user doesn't enter in their age, their age isn't 0. If a gps coordinate is not currently available, [the coord is not 0](https://www.youtube.com/watch?v=bjvIpI-1w84). Additionally, making Swift more C-like should not at all be a goal. C is highly concerned with the workings of the hardware, and drives the programmer to design around that. It doesn't have the abstraction for high level program design. – Alexander Feb 11 '19 at 15:45
  • Yes, I’d like to undo this response to what may be perceived as implicit or ambiguous conversions. In many applications nil values often from system frameworks can reasonably be expected to be non-nil. Take for example `keyWindow` on `UIApplication`. Unwrapping these values just contributes to boilerplate when having a dumb default to 0 would ease usage. – Ilias Karim Feb 11 '19 at 16:30
  • I don't understand this comment about `keyWindow`. It's completely legitimate for `keyWindow` to be nil, and you often need to deal with that fact. It's nil before the application finishes launching, and I've seen several people create bugs due to assuming that their code can't run at that point (which is not true; there's all kinds of user code and even UI code that can run before the app finishes launching; state restoration is notorious for this). Also, `keyWindow` is not an Int, so how would this help? And what boilerplate would involve that? – Rob Napier Feb 11 '19 at 16:42
  • If you do have examples of boilerplate you're trying to remove, it's probably worth asking questions about those. There are often very good ways to reduce repetition. (Not always, but very often.) – Rob Napier Feb 11 '19 at 16:44
  • I’m sorry that you don’t understand, Rob. – Ilias Karim Feb 11 '19 at 19:33
1

I'm not sure I would recommend doing this, but you could do something like this, as an example for integers to first check if it's nil and if so just compare with 0. You would have to repeat this thing for flipped parameters and the same thing for other types.

extension Optional where Wrapped == Comparable {
    public static func < (lhs: Wrapped?, rhs: Int) -> Bool {
        guard let left = lhs as? Int else {
            return 0 < rhs
        }
        return left < rhs
    }
}