For example, if you take a sequence like a: Int?[] = [0, nil, 1, nil], and attempt to iterate over it, the generator has to give you values of type Int??. And, not only that, it has to be able to distinguish between an Int?? value that's nil (meaning iteration is done) and an Int?? value that has a real Int? value that happens to be nil (meaning it's just giving you a[1]).
This is a lot easier to think about in a language that doesn't try to hide the nilness. For example, in Haskell, if you have a Maybe Maybe Int, it could have any of these values:
- Nothing
- Just Nothing
- Just Just 0
- nil
- nil
- 0
- Int?? = nil
- Int?? = nil
- Int?? = 0
if let a = foo { println("\(a)") } else { println("really nothing") }
This will print:- really nothing
- nil
- 0
let a: Int?[] = [0, nil, 1, nil]
for i in a { println("\(i)") }
var g = a.generate()
while let i = g.next() { println("\(i)") }
Except that once generic functions get involved, everything breaks. Try either of the following:
func printem(s: S) {
var g = s.generate()
while let i = g.next() { println("\(i)") }
}
printem(a)
func printem(s: S) {
for i in s { println("\(i)") }
}
printem(a)
If you try to call either version with a sequence whose element type is optional, you get a compiler crash. In the first case, I think it's crashing while trying to infer types to compile the let binding, but putting an explicit i: T there doesn't change anything, so… who knows.Oddly, if the type of a isn't an Array of optionals, but a custom struct, instead of a crash, it seems to successfully compile the wrong code, trying to bind the T?? directly to a T, and therefore treating both nil values and the end of the sequence the same.
When I've needed to dig into Swift Optionals and do things based on their exact structure, I've found it easiest to treat them as their true nature— enums.
ReplyDeleteHere's a quick function I cooked up that differentiates between your three types just switch/case-ing on the Int??
func printType(_ ooi:Int??) {
switch ooi {
case .some(.some(let i)):
print("\(i)")
case .some(.none):
print("nil")
case .none:
print("really nothing")
}
}
printType(nil) // prints "really nothing"
printType(Int?.none) // prints "nil"
printType(0) // prints "0"