Can someone explain what is happening in the code below? It creates a struct Either
which takes any two optional types and (attempts to) return whichever one is not nil, or the first if both are not nil. However, it behaves strangely when passing literal nil as opposed to a nil variable in. I can't see why b4
in the example behaves as it does...
struct Either <T1, T2> {
let first: T1?
let second: T2?
init(first: T1?, second: T2?) {
self.first = first
self.second = second
}
func either() -> Bool {
return (self.first != nil) || (self.second != nil)
}
func which() -> Any? {
if self.first != nil {
return self.first
} else if self.second != nil {
return self.second
}
return nil
}
}
var s1: String? = nil
var s2: Int? = nil
let b1 = Either(first: s1, second: s2)
b1.either() // false
b1.which() // {nil}
s1 = "Hello"
let b2 = Either(first: s1, second: s2)
b2.either() // true
b2.which() // {Some Hello}
s1 = nil
s2 = 7
let b3 = Either(first: s1, second: s2)
b3.either() // true
b3.which() // {Some 7}
// all as expected, however
let b4 = Either(first: nil, second: nil)
b4.either() // true !!! <<<<<<<<<<<<<<<<<<
b4.which() // {nil}
I think it has to do with "Optional Optionals", but I'm not sure it's behaving as expected.
This has been fixed in Beta 3, the last case now correctly triggers a compiler error forcing you to specify the generic type explicitely
The problem is in types - when creating Either
from literals:
let b4 = Either(first: nil, second: nil)
then the compiler can't infer the type for the nil
parameters. If you check the debugger, you will see that the inferred type is a type called _Nil
.
(lldb) p b4
(SwiftConsole.Either<_Nil, _Nil>) $R1 = {
first = Some
second = Some
}
Now, this is a type which accepts nil
but it is not an optional type itself.
let x: _Nil = nil //this is not an optional
The initialiser is making an optional of it, thus making a value Optional.Some(nil)
and because it's a .Some(...)
value, comparing it with nil
will be false
.
I don't see any easy generic workarounds but in this case it would help to specify generic types explicitly:
let b4 = Either<String, Int>(first: nil, second: nil)
I suggest you report a bug because this is just silly. Instead of inferring some special types which lead to undefined behaviour, the compiler should instead trigger an error.
The same problem would arise with type Any
because it can also accept nil
without being an optional.
let x: Any = nil //this is not an optional
Either<Any, Any>(first: nil, second: nil).which() //true !!!
Edit:
This will be fixed in Beta 3. nil
will be made a literal, _Nil
type will be removed and _Any
won't accept nil
any more.
Confirmed here: https://devforums.apple.com/thread/234463?tstart=0
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With