SE-0231 — Optional iteration

The review of SE-0231 — Optional Iteration begins now and runs through October 12, 2018.

I've written a mid-review summary of the discussion thus far. If you want to know whether a particular idea has already been discussed without reading the entire thread, you can consult this summary comment.

Reviews are an important part of the Swift evolution process. All review feedback should be either on this forum thread or, if you would like to keep your feedback private, directly to the review manager (via email or direct message in the Swift forums).

What goes into a review of a proposal?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift.

When reviewing a proposal, here are some questions to consider:

  • What is your evaluation of the proposal?
  • Is the problem being addressed significant enough to warrant a change to Swift?
  • Does this proposal fit well with the feel and direction of Swift?
  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Thanks,
Joe Groff
Review Manager

6 Likes

What is your evaluation of the proposal?

+1 but with a strong preference for the for element in elements? {…} syntax

Is the problem being addressed significant enough to warrant a change to Swift?

I think the proposal hits a common pain-point. I have done the for e in optionalSequence ?? [] dance a lot.

Does this proposal fit well with the feel and direction of Swift?

The alternative syntax for element in elements? {…} fits nicely (similar to the pattern matching in a case binding) while I dislike the directly proposed syntax using for? because there is no precedent for this.

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

Nope

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Read through the proposal and followed the original discussion partially.

3 Likes

I'm a big +1 on this. This is something I reach for very often. My only feedback would be the location of the ?. I would rather it be placed on the thing being iterated over, as mentioned in the alternatives. Or alternatively placing it on in. My reasoning is I don't think of this as a special kind of for-in loop, but that for-in loops just have this behavior when the iterator is optional.

7 Likes

I'm in favor of the problem this proposal is aiming to solve, but not necessarily the solution. As the proposal states, switch statements aren't followed by ? for optional types, so it might be helpful if for loops could offer the same (for element in sequence). I don't think the behavior of such would surprise anyone ("for loop over an optional sequence, if its there"). The proposal doesn't state how nested optionals are treated, does one for? iterate over [Int]??? ? Or do I need for??? ? This is where the proposed syntax breaks down for me. Others might ask why a single ? is able to iterate over a [Int]??.

let nums: [Int]??? = [1, 2, 3]

// Wouldn't I need for??? for all 3 optionals?
// Am I iterating a [Int]??
for? num in nums {
  print(num)
}

I think the switch statement solves a lot of those problems by simply not requiring any such syntax:

let x: Int????????? = 16

switch x {
case 16:
  print("I'm the number 16")
case nil:
  print("I'm nil")
default:
  print("I can tell you that I'm no 16 nor nil")
}

// I'm the number 16

It just works.

Also, it might seem awkward for learners to find out that theres no for! (whatever that means).

Therefore I'm in favor of a solution that requires no new syntax. for element in optionalSequence should just work.

Off the top of my head, I can't think of a time where I needed this, but I absolutely see the use case and agree that this is a problem worth solving.

As I stated above, I'm in favor of the problem the proposal is aiming to solve, just not necessarily the solution.

I read the proposal with some in-depth thought.

15 Likes
  • What is your evaluation of the proposal?

It’s basically “Sure, why not?”

There’s currently no consistently available way to do what for? would do without adding more control flow around the loop. I don’t feel like that’s a gaping hole in the language, but it is an irritation.

I think it would be better to put the question mark after the sequence being iterated instead of after the for keyword, because that’s where you would put the ! if you were force-unwrapping or the ?? if you were nil-coalescing. Let’s not make you look at two different places to figure out how a loop interacts with an optional. It’s also where I think a user who had never been told this feature was available would guess it should go (although that’s just my gut).

I don’t think this is urgently necessary, but I think it pulls its weight.

  • Is the problem being addressed significant enough to warrant a change to Swift?

Maybe. I haven’t been pining for this feature, but I would probably have used it a couple of times if I’d had it available. It seems like a small enough change that the bar should be set relatively low.

  • Does this proposal fit well with the feel and direction of Swift?

Yes. I think the alternate position of the ? would fit even better, though.

  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

As usual, optionals are much nicer than languages where any object can be nil; they all choose either ?-style or !-style behavior as an implicit, mandatory part of the language. Swift wins by making this an explicit choice instead.

  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Glance, although I followed the previous discussion a little.

3 Likes

I'm a minor -1 on this proposal. I don't see much value over this pretty common approach I've seen and used (which isn't mentioned in the proposal):

for element in array ?? [] { ... }
3 Likes

It's in the Motivation section, which alludes to but doesn’t spell out the drawbacks.

Ah, I missed that. Yeah, some mention of the drawbacks would be helpful.

I have revised my opinion on this proposal. For posterity, my initial response is kept in here, but is collapsed. TL;DR: I don't think we should do this.
  • What is your evaluation of the proposal?

+0.5, with revision. I'd prefer seeing the syntax as "for item in? optionalArray" over the proposed for? syntax. Putting the ? on the preposition feels more inline with existing syntax around as, as?, and as!.

I'm not a full +1 on this because I wish I could just do for item in optionalArray and have the compiler do the expected thing of checking for nil for me.

  • Is the problem being addressed significant enough to warrant a change to Swift?

I personally have not really felt like this was ever really an issue for me, but my experience with swift is not necessarily idiomatic, so I will defer on answer this.

  • Does this proposal fit well with the feel and direction of Swift?

Yes.

  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

My only other main language is Objective-C, where for (id item in potentiallyNullArray) just doesn't do anything; no special syntax is required. IMO that would be the far "better" solution than adding more syntax. I get that it was dismissed as being confusing, but I would personally prefer that.

  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

I've been lurking on the discussion thread and read the proposal.

3 Likes

To avoid additional nesting, you can coalesce with ?? [] for Array only

I guess I should've been more verbose, but the drawback is that ?? [] can only be used with Array. Or, to be strict, only with sequences that conform to ExpressibleByArrayLiteral. Another drawback would be the instantiation of [] itself and the ergonomic aspect of having ?? [].

2 Likes

What is your evaluation of the proposal?

I'm in favor.

Is the problem being addressed significant enough to warrant a change to Swift?

It might be just a convenience, but wanting to skip a loop when a value is nil is so common it deserves to be made simple. So yes.

Does this proposal fit well with the feel and direction of Swift?

I believe the ? should go after the expression we want to iterate on, at the same location you'd be inserting ! if you wanted to force-unwrap.

At a glance, for? might look like it has try? as a precedent. But since we have try! but no for!, it's not the right precedent to follow. Better inspire ourselves from the optional chaining syntax and how similar it is when you use !.

Moreover, if you have nested optionals, you should be able to write array??, like you can with optional chaining (array??.count), and like array!! to force unwrap two levels.

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

N/A

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Read the proposal and participated a bit in the previous discussions.

Big +1 - iteration might be one of last places where force unwrapping is much more easy than the "better" alternative, so there's room for improvement:

  • if let / guard let are straightforward, but extremely verbose
  • optionalSequence?.forEach isn't a loop (no break, no continue)
  • for element in optionalSequence ?? [] isn't that much bigger in terms of verbosity, but has a useless allocation of an empty array (at least in the source) - and doesn't work for all Sequences

The only flaw I see with the proposal has been pointed out already: for? doesn't look like the best choice...
There have been some discussions before, but afair no one proposed this spelling.
The question mark is far away from the Optional, and imho that's unfortunate.

The most obvious alternatives

for element in optionalSequence?
has a equivalent with an exclamation mark, which has some precedent (try?/try!, optional chaining)

for element in? optionalSequence
reminds me on if let x = x as? Type (which isn't spelled if let? x = x as Type either)

Personally, I like in? - but I think the duality of optionalSequence? and optionalSequence! is compelling.

The alternative to allow Optional<Sequence> to be used in loops just like the non-optional type had some backlash in past discussions, and imho it doesn't hurt to type a single additional character which makes clear that the loop deals with an Optional:
Users might not even be aware of that, and they might prefer empty collections over nil values in their code.

7 Likes

That's easy to say for when you're iterating an array. What if you wish to iterate a database cursor? How do you instantiate an empty database cursor?

1 Like

?? [:] works for dictionaries too.

-1

I am strongly opposed to adding for? or in? . I just do not think the sugar here is needed.
Full disclosrue I am also oposed to optional collections in a code base.

Why? too many question marks. it deludes its meaning.

I get it, people do not like the if let, okay. use a one liner guard, succinct, clear.

or use the ?? to unwrap, there should be no preformance issues if you give it an empty collection becuase that is a noop operation for iteration.

The problem is not significant. Not change warranted.

Do we really want to keep using the question mark for more sugar?

in other languages if you iterate over a null object, it will crash.

read the proposal.

1 Like

True, this would be a more generic solution and I don't see its addition causing any harm.

  • What is your evaluation of the proposal?

-1

Speaking from experience, more likely than not, an optional sequence is a sign of ill-considered representation of the underlying data (where an empty sequence is sufficient to represent the lack of elements). There are rare occasions where an extra state has legitimate meanings, but I have a hard time coming up with real-world examples from the few large projects I work on.

Introducing a syntax for this use-case makes it easier to do the wrong thing. And it addresses very few legitimate use cases.

In general, I'm also strongly against any more syntax exceptions made for Optional. As far as I can tell, theres no discussion of how this new syntax can be generalized for other types.

  • Is the problem being addressed significant enough to warrant a change to Swift?

No

  • Does this proposal fit well with the feel and direction of Swift?

No

  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

N/A

  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

A quick read.

14 Likes

What is your evaluation of the proposal?

+1, though, like others, I prefer the ? on the value being enumerated, not on the for or in, as it matches the pattern matching using of ?.

Is the problem being addressed significant enough to warrant a change to Swift?

It's a minor issue, but I think this would bring nice symmetry to the language if the ? was on the value.

Does this proposal fit well with the feel and direction of Swift?

Yes, sugar around explicit acknowledgement of Optional values is a very Swifty thing.

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

None that I know of.

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Participated in the pitch thread, read the proposal.

4 Likes

-0.5 -- I don't think the problem being addressed is significant enough to warrant a change, but I don't feel very strongly about it. ?? [] is a short and idiomatic solution, and I would expect any reasonably full-featured Sequence should also conform to ExpressibleByArrayLiteral, so I think that the circumstances where this existing solution applies is pretty broad.

If the core team does go ahead with this, then I'd greatly prefer a change where there are no syntactic differences at all. I.e. either for automatically handles optionals the way that switch does, or the standard library handles it via:

extension Optional : Sequence where Wrapped : Sequence {
    public typealias Element = Wrapped.Element
    public typealias Iterator = AnyIterator<Element>

    public func makeIterator() -> Iterator {
        switch self {
        case .none:
            return AnyIterator({ return nil })
        case .some(let wrapped):
            return AnyIterator(wrapped.makeIterator())
        }
    }
}

The semantics of what should happen are so straightforward that new keywords (for? / in?) or extra sprinkling of ? on the sequence reference are just extra noise.

10 Likes

I haven't had a chance to look closely at the final proposal yet, but I strongly disagree with this. There are many kinds of sequences that inherently cannot conform to ExpressibleByArrayLiteral. Not all sequences are collections that store individual values. In fact, I think this distinction is one of the best arguments in favor of adding syntactic sugar in this area. There may be sequences for which it is not easy (or even possible) to initialize an "empty" value.

9 Likes