SE-0231 — Optional iteration

I am a +1 on this proposal (except for the ? location). It is fairly common to get a sequence from a dictionary or optional chaining, and a lightweight syntax for handling it that fits with the many other uses of ? would be appropriate.

You could make the same argument about optional chaining in general – why should you have to write a question mark? Send-to-nil doing nothing has its fans, but it's a style that is explicitly eschewed by Swift. Silent handling of nil should be acknowledged, via the ? sigil that has clear meaning wherever it appears. That is exactly what this proposal would allow, for iteration.

The analogy with switch is missing a key difference: you must still explicitly handle all cases (even if that explicit handing is sticking in a default: clause). You cannot just write this, for example:

let b: Bool? = true
switch b { // Error: Switch must be exhaustive
case true: print("yay")
case false: print("boo")
}

An the analogy to silent optional iteration might work if the switch were just skipped over in the case of nil. This is not what happens with switch, and it should happen with iteration either.

Also, bear in mind the reason switch (1 as Int?????) { case 1: } works is because of Optional's conditional conformance to Equatable. Any Equatable value can be matched in a switch via the ~= that works on any equatable T. The equivalent for iteration would be to have Optional: Sequence where Wrapped: Sequence, with a flattening behavior for optionals of sequences. Which leads to more questions: should .some([1,2,3]).map(...) produce a non-optional array? Should filter?

It is definitely true that returning an optional array from a function is a common beginner mistake. But it's also sometimes the right choice. For example, when nil indicates failure, or "not specified" as a distinct value to specified as empty. Being able to confirm you want nil to mean the same as empty in your handling, via a simple bit of syntax consistent with other places where nils are acknowledged, but then then handled silently, seems in keeping with the rest of the language.

Placement of the ?

Placing the ? after the keyword is an inappropriate solution. ~I don't believe there are any other cases where ? is used with a keyword rather than a type or a variable.~ edit: doh, of course there are – e.g. try?.

The consistent placing should be on the sequence variable:

a!.doSomething()   // trap if a is nil
a?.doSomething()   // do nothing if a is nil
a![1] = 2  // trap if a is nil
a?[1] = 2  // do nothing if a is nil
a!.forEach { ... }  // trap if a is nil
a?.forEach { ... }  // do nothing if a is nil
for x in a! { ... }  // trap if a is nil
for x in a? { ... }  // do nothing if a is nil

The consistency here also gives me no qualms about explaining this to beginners.

18 Likes