Sequence
is a particularly important protocol, because it governs which types can be iterated in a for
loop. To this end, I think it is worthwhile to make Optional
conform to Sequence
when its wrapped value conforms to Sequence
.
In general, I don't like the idea of the language dictating default behavior, because the default semantics might not be universally desirable. In this case, however, I think this has very universal, obvious semantics:
- If there is something to iterate: iterate it
- If there is nothing to iterate: don't iterate it
Here's a sample implementation:
public extension Optional: Sequence where Wrapped: Sequence {
public typealias Element = Wrapped.Iterator.Element
public typealias Iterator = AnyIterator<Wrapped.Iterator.Element>
public func makeIterator() -> AnyIterator<Wrapped.Iterator.Element> {
return self.map { AnyIterator($0.makeIterator()) }
?? AnyIterator(EmptyCollection().makeIterator())
}
}
Usage:
let a: [Int]? = [1, 2, 3]
for i in a {
print(a)
}
I found this particularly useful when you wish to do something for every element in a sequence returned by a throwing method. Compare the before/after:
func throwingFunc() -> [1, 2, 3] { return [1, 2, 3] }
// before:
let result = try? throwingFunc()
if let result = result {
for element in result {
print(result)
}
}
// after:
for element in try? throwingFunc() {
print(element)
}
Now, you might be thinking, "just use the nil coalescing operator to default to an empty collection ([]
or ""
)!" And this is possible, for Array
, Set
, Dictionary
, String
. But what about other sequence/collection types? How do you make an empty UnfoldSequence<Int, (Int, Int)>
? sequence(state: (0, 0), next: { _ in nil })
? Yikes!