[Rejected] SE-0231 - Optional iteration

Although the core team agrees that iterating through collections wrapped in Optional is a common enough occurrence to be worth providing affordances in the language for, it has decided to reject proposal SE-0231 as written. The proposed syntax for? x in xs would be a new special case for users to learn and understand, would be difficult to discover or search for, and would not generalize well to things like double-Optionals. Nonetheless, the review discussion made it clear this is a problem worth solving; many people data-mined their projects and counted many occurrences of if let conditions followed by for loops or similar formulations that would benefit from being made more compact. Many alternatives for addressing the issue came up in the discussion, and of these, the core team most strongly favors the idea of extending optional chains to include for loops, allowing for the following:

let optionalSequence: [Int]?
let optional: (sequence: [Int], otherStuff: Int)?

for x in optionalSequence? { ... }
for x in optional?.sequence { ... }

The core team prefers this approach to the alternatives for a number of reasons:

  • This extends an existing mechanism instead of introducing new syntax.
  • As many people in the review thread noted, it's usually an anti-pattern to have an optional sequence as a first-order value; seldom is it interesting to differentiate "no value" from "empty value". However, even following this principle, it's common to end up with second-order optional sequences as a result of composing other operations, particularly common things like getting a collection out of a dictionary or by an optional-chained access through some other legitimately optional property. Optional chaining directly addresses this most common use case for the feature.
  • Optional chaining composes well with itself, and also composes to work with double-optionals. It is possible to address this use case purely in the library, by adding an orEmpty property to Optional<T: Sequence> that produces a wrapper sequence, but that would not compose well with optional chains, requiring someone to use extra parens and write (x?.y).orEmpty() to wrap the optional chain.

There are nonetheless legitimate concerns to address with extending optional chaining to for loops. Although it extends an existing language feature, it does so in a non-obvious and potentially confusing way. It could also be seen as establishing a "slippery slope" to creeping optional chaining into other contexts. Although optional chaining generally composes well, it still gets ugly in some cases, particularly if one were to use the try? operator in a for loop:


for x in (try? operationThatMightThrow())? { ... }

In response to that last point, one could point to the precedent recently set by SE-0230, flattening the nested optionals produced by try?, and argue that try? effectively begins an optional chain of its own. In order to thoroughly investigate these concerns and converge on an ideal design, the core team would like to see a revised proposal for this optional-chaining-based design and run it through review again. Thanks everybody who contributed to this review!

27 Likes