Auto-wrapping into optional types

(This question arises from a discussion at http://stackoverflow.com/questions/33216358/swift-operators-and-nil.)


Optionals can be compared if the underlying wrapped type is comparable:


public func <<T : Comparable>(lhs: T?, rhs: T?) -> Bool


But this compiles as well:


func foo(x : Int?, _ y : Int) {
    let b = x < y
    print(b)
}


and it seems that the Int on the rhs is "auto-wrapped* into Optional<Int>, using the init method


/// Construct a non-`nil` instance that stores `some`.
public init(_ some: Wrapped)


But why? The Swift book clearly states that "Values are never implicitly converted to another type." Also in other cases, like comparing Int and Double, no auto-wrapping from Int to Double or vice versa is done. I am probably missing something obvious, but I cannot see from the Swift documentation why this works in the case of optionals.

Don't think of it as an operand conversion; think of it as the comparison operators being defined on combinations of optional and nonoptional Ts.


Nil has a defined collation order. It's lower than any .Some of the same type.

But in Swift all possible operator/operand combinations are defined explicitly. On https://developer.apple.com/library/ios//documentation/Swift/Reference/Swift_StandardLibrary_Operators/index.html there is a "less-than" operator taking two optionals (the one at the start of my question). There is no "less-than" operator taking an optional and a non-optional.

Optionals are handled specially in Swift. The fact that it is implemented as an enumeration is an implementaion detail, but the language itself provides conveniences for working with optionals so you can do things like:

var x : Int? = 4

and

for case let x? in array {...

The language allows a convenience for a non-optional to be automatically converted to an optional as in my first example.


Automatically converting between numeric types can have unintended side-effects because different types have different ranges. Also, it isn't always clear what a numeric type really is (due to aliases) unless you go digging. So, having the type checking can be helpful. I don't really find the numeric conversions to be so problematic.


Automatically, converting a non-optional to an optional is a relatively benign convenience. Implicitly unwrapped optionals is definitely more dangerous, but still a convenience.

MartinR is right, there is no < operator taking an optional and non-optional.


In cases like this you can always cmd-click (or ctrl+cmd+J) on something to see its declaration, so doing that on the < operator in this context

let a = Optional<Int>(1) < 2


will take you to this declaration in the stdlib generated interface:

@warn_unused_result
public func <<T : Comparable>(lhs: T?, rhs: T?) -> Bool

You can think of it as wrapping, or you can think of it as T being a subtype of T?, etc. Similar to these working

class C: Comparable { }

class S1: C { }
class S2: C { }

let  c =  C()
let s1 = S1()
let s2 = S2()

s1 < c
s1 < s2 // etc


Optionals would be much more annoying to work with if they weren't treated like this, but there are some tradeoffs/possible pitfalls, as your example shows.

Auto-wrapping into optional types
 
 
Q