Does an equality operator imply forced unwrapping? - swift

Note the last two lines:
var optionalString: String? // nil
optionalString == "A" // false
//optionalString! == "A" // EXC_BAD_INSTRUCTION
optionalString = "A" // “A”
optionalString == "A" // true
optionalString! == "A" // true
Does that mean that we are not required to unwrap an optional when comparing?

This is the definition of the == operator that looks like it gets used in this case:
public func ==<T : Equatable>(lhs: T?, rhs: T?) -> Bool
You see that both the first argument (lhs) and the second argument (rhs) have to be of the same type, T?. Since the first argument (optionalString) is String?, I think the second argument is cast to String? too, which makes the comparison work.
I think this proves the idea:
func testCasting<T: Equatable>(a: T?, b: T?) {
print(a, b) // Optional("A") Optional("A")
}
var optionalString: String? = "A"
testCasting(optionalString, b: "A")
In the second argument you pass a literal A that gets wrapped in an optional to make the types check. The code compiles and runs.
Note that this is completely different from unwrapping the first argument implicitly. That wouldn’t be a safe operation and would undermine the whole point of optionals.

Apparently one can compare any optional to nil.
optionalString == nil
Which is true if an optional is nil now.
Even this works:
if "A" != nil {
print("A")
}

Related

Unwrap/coalesce a multi-level optional

I'm trying to write a function to unwrap optionals with an arbitrary number of levels of nesting. Here's the test I'm using:
let a: Int??? = 1
let b: Int??? = nil
print(a.unwrap(0), b.unwrap(0)) // should print 1, 0
I can get the correct output with a basic generic function:
extension Optional {
func unwrap<T> (_ defaultValue: T) -> T {
return (self as? T) ?? defaultValue
}
}
print(a.unwrap(0), b.unwrap(0)) // 1, 0
But this doesn't prevent the function from being called with a different type than the optional. For instance, I could call a.unwrap("foo") and it would print "foo" instead of "1" since of course you can't cast Int??? to String.
I tried it using Wrapped instead, which semi-properly restricts the default value but doesn't give the correct output:
extension Optional {
func unwrap (_ defaultValue: Wrapped) -> Wrapped {
return (self as? Wrapped) ?? defaultValue
}
}
print(a.unwrap(0), b.unwrap(0)) // Optional(Optional(1)), nil
It only unwraps one level of the optional, instead of all three, and since nil is a valid value for Int?? it doesn't return the default.
Is there any way to safely do what I want here?
This code does what you ask for. The drawback is that you need to implement the Unwrappable protocol in every type you want to put inside optionals. Maybe Sourcery can help with that.
protocol Unwrappable {
associatedtype T
func unwrap(_ default: T) -> T
}
extension Optional {}
extension Optional: Unwrappable where Wrapped: Unwrappable {
typealias T = Wrapped.T
func unwrap(_ defaultValue: T) -> T {
if let value = self {
return value.unwrap(defaultValue)
}
return defaultValue
}
}
extension Int: Unwrappable {
typealias T = Int
func unwrap(_ default: Int) -> Int {
return self
}
}
let nestedOptionalValue: Int??? = 6
let nestedOptionalNil: Int??? = nil
let optionalValue: Int? = 6
let optionalNil: Int? = nil
print(nestedOptionalValue.unwrap(0)) // prints 6
print(nestedOptionalNil.unwrap(0)) // prints 0
print(optionalValue.unwrap(0)) // prints 6
print(optionalNil.unwrap(0)) // prints 0
The trick is that the Unwrappable protocol marks types that can be eventually unwrapped (as in after 0, 1 or more unwraps) to a certain type.
The difficulty in this problem comes from the fact that in an extension to Optional, you can get the Wrapped type, but if Wrapped is optional again, you can't access Wrapped.Wrapped (in other words, Swift doesn't support Higher Kinded Types).
Another approach, would be to try to add an extension to Optional where Wrapped == Optional. But again you'd need Higher Kinded Types support to access the Wrapped.Wrapped type. If we try to extend Optional where Wrapped == Optional<T>, we also fail, because we cant extend Optional generically over T.

Swift optionals and equality operator

Looking for a doc reference or a name or link on this particular behavior, which is similar to optional binding but isn't talked about in that part of the docs.
I can test an optional with the == operator, and test against both nil and its actual value, without doing any explicit unwrapping:
var toggle: Bool? = nil
if (toggle == true || toggle == nil) {
// do something
}
This compiles and works as you'd want it to, but what's happened here is that I haven't had to unwrap toggle! explicitly; the == has safely done it for me.
It's convenient but I confess to being a little surprised when I noticed it. Is this just a behavior of the default == implementation? Or is something else in the language happening here? Thanks for insight.
Swift has an equality operator taking two optionals values
(of an Equatable base type):
public func ==<T : Equatable>(lhs: T?, rhs: T?) -> Bool
The implementation can be found at Optional.swift:
public func == <T: Equatable>(lhs: T?, rhs: T?) -> Bool {
switch (lhs, rhs) {
case let (l?, r?):
return l == r
case (nil, nil):
return true
default:
return false
}
}
and it does what one would expect: The operands are equal if they
are both nil, or if they are both not nil and the unwrapped
values are equal.
Similar comparison operators < etc taking optionals have been
removed in Swift 3, compare
SE-0121 Remove Optional Comparison Operators:
Remove the versions of <, <=, >, and >= which accept optional operands.
Variants of == and != which accept optional operands are still useful, and their results unsurprising, so they will remain.
So this works as expected:
let b: Bool? = nil
print(b == true) // prints "false"
But as matt pointed out, this can not be done with implicitly unwrapped
optionals, here the left operand will be unwrapped:
let b: Bool! = nil
print(b == true) // crashes

Swift Optional type: how .None == nil works

I'm trying to understand how does it work:
1> func returnNone() -> String? { return .None }
2> returnNone() == nil
$R0: Bool = true
3> returnNone() == .None
$R1: Bool = true
Why .None is equal nil.
I don't see anything about it in enum definition:
public enum Optional<Wrapped> : _Reflectable, NilLiteralConvertible {
case None
case Some(Wrapped)
/// Construct a `nil` instance.
public init()
/// Construct a non-`nil` instance that stores `some`.
public init(_ some: Wrapped)
/// If `self == nil`, returns `nil`. Otherwise, returns `f(self!)`.
#warn_unused_result
#rethrows public func map<U>(#noescape f: (Wrapped) throws -> U) rethrows -> U?
/// Returns `nil` if `self` is nil, `f(self!)` otherwise.
#warn_unused_result
#rethrows public func flatMap<U>(#noescape f: (Wrapped) throws -> U?) rethrows -> U?
/// Create an instance initialized with `nil`.
public init(nilLiteral: ())
}
enum Optional conforms to the NilLiteralConvertible protocol,
which means that it can be initialized with the "nil" literal.
The result is Optional<T>.None where the type placeholder T
must be inferred from the context.
As an example,
let n = nil // type of expression is ambiguous without more context
does not compile, but
let n : Int? = nil
does, and the result is Optional<Int>.None.
Now optionals can in general not be compared if the underlying
type is not Equatable:
struct ABC { }
let a1 : ABC? = ABC()
let a2 : ABC? = ABC()
if a1 == a2 { } // binary operator '==' cannot be applied to two 'ABC?' operands
and even this does not compile:
if a1 == Optional<ABC>.None { } // binary operator '==' cannot be applied to two 'ABC?' operands
But this compiles:
if a1 == nil { }
It uses the operator
public func ==<T>(lhs: T?, rhs: _OptionalNilComparisonType) -> Bool
where _OptionalNilComparisonType is not documented officially.
In https://github.com/andelf/Defines-Swift/blob/master/Swift.swift the
definition can be found as (found by #rintaro and #Arsen, see comments):
struct _OptionalNilComparisonType : NilLiteralConvertible {
init(nilLiteral: ())
}
This allows the comparison of any optional type with "nil", regardless of whether the underlying type is Equatable or not.
In short – in the context of Optional – nil can be thought of as a shortcut to .None, but the concrete type must be inferred from the context. There is a dedicated == operator for comparison with "nil".

Check array of optionals for nil

I have a number of optionals and would like to efficiently check to see if any of them is nil.
Ideally...
if contains([optional1, optional2, optional3], nil) { /* foo */ }
But swift won't let me do this. (Type 'S.Generator.Element -> L' does not conform to protocol 'NilLiteralConvertible')
And optionals can't be AnyObjects so I can't cast it to an NSArray either (to use .containsObject).
I could just write a for loop but that seems like bad style. Any suggestions?
There are two contains() functions:
func contains<S : SequenceType where S.Generator.Element : Equatable>(seq: S, x: S.Generator.Element) -> Bool
func contains<S : SequenceType, L : BooleanType>(seq: S, predicate: #noescape (S.Generator.Element) -> L) -> Bool
The first one takes an element as the second argument, but
requires that the elements conform to the Equatable
protocol, which is not the case for optionals.
The second one takes a predicate as second argument, and that
can be used here:
let optArray : [Int?] = [1, nil, 2]
if contains(optArray, { $0 == nil } ) {
}
But any solution must traverse the array until a nil element is
found, so the above solution may be better readable but is not
necessarily more efficient than a for-loop.

Swift error comparing two arrays of optionals

I get a compilation error in the next Swift code
var x:Array<Int?> = [1,2]
var y:Array<Int?> = [1,2]
if x == y { // Error
}
If both arrays are Array<Int> it works fine, but if at least one of them is optional it throws an error like the next:
Binary operator '==' cannot be applied to two Array<Int?> operands
I filed a bug report months ago but I had no answer. It still occurs in Swift 1.2.
Why is this happening?
The issue here is the distinction between something having an == operator, versus something being “equatable”.
Both Optional and Array have an == operator, that works when what they contain is equatable:
// if T is equatable, you can compare each entry for equality
func ==<T : Equatable>(lhs: [T], rhs: [T]) -> Bool
// if T is equatable, you can compare the contents, if any, for equality
func ==<T : Equatable>(lhs: T?, rhs: T?) -> Bool
let i: Int? = 1
let j: Int = 1
i == j // fine, Int is Equatable
["a","b"] == ["a","b"] // and so is String
But they themselves do not conform to Equatable. This makes sense given you can put a non-equatable type inside them. But the upshot of this is, if an array contains a non-equatable type, then == won’t work. And since optionals aren’t Equatable, this is the case when you put an optional in an array.
You'd get the same thing if you tried to compare an array of arrays:
let a = [[1,2]]
let b = [[1,2]]
a == b // error: `==` can’t be applied to `[Array<Int>]`
If you wanted to special case it, you could write == for arrays of optionals as:
func ==<T: Equatable>(lhs: [T?], rhs: [T?]) -> Bool {
if lhs.count != rhs.count { return false }
for (l,r) in zip(lhs,rhs) {
if l != r { return false }
}
return true
}
For a counter-example, since Set requires its contents to be hashable (and thus equatable), it can be equatable:
let setarray: [Set<Int>] = [[1,2,3],[4,5,6]]
setarray == [[1,2,3],[4,5,6]] // true