[swift-evolution] A (better) Swift Equivalent For The Classical For-Loop With Numeric Scalars

Dany St-Amant dsa.mls at icloud.com
Wed Mar 23 21:41:31 CDT 2016


> Le 22 mars 2016 à 21:25, Brent Royal-Gordon <brent at architechies.com> a écrit :
> 
>> These may be compact, but some programmer may fell that they are not in control: do these "stdlib" seq(), c.filter() pre-calculate all the entries (wasting precious cpu cycle if one break out early)  or are each value dynamically created? Having an explicit legacy loop format gives a feel of control. So something like
>> 
>> for i from 2 to 1_000_000 by 1 where i % 2 != 0 while foundCount < 5 { print(i); foundCount +=1 }
>> 
>> sounds less magical and seems easier to predict than
>> 
>> for i in 2.stride(to:1_000_000, by:1).filter({ $0 % 2 != 0}).prefix(5) { print(i) }
>> 
>> and is still quite readable, even if it mixes for loop, while loop and even simple condition.
> 
> First of all, this example sucks. There are *many* ways to write it with existing features which would be more understandable than the pile of random code in your `for` loop. Here are a few:
> 
> 	for i in stride(from: 2, to: 1_000_000, by: 1) where i % 2 == 0 {
> 		print(i)
> 		foundCount += 1
> 		if foundCount >= 5 { break }
> 	}
> 
> 	for i in stride(from: 2, to: 1_000_000, by: 2) {
> 		print(i)
> 		foundCount += 1
> 		if foundCount >= 5 { break }
> 	}
> 
> 	for i in stride(from: 2, to: 1_000_000, by: 2).prefix(5) {
> 		print(i)
> 	}
> 
> I know you're trying to illustrate some features you'd like on the `for` loop, but by choosing this example you're not realy challenging your proposal enough for its disadvantages to be illustrated well. For instance, one thing `stride` cannot currently do is apply a step that doesn't involve addition, for instance by multiplying the number. But your `by` keyword suffers from the same problem! To really add anything new, your proposal would have to be vastly more complicated than what you describe here. It's not fair to say "look how much simpler this is" while sweeping the actual complexity under the rug.

I agree with the qualitative you used against my example, but not sure it was necessary to say it out loud. The main thing I was trying to illustrate is the at glance interpretation and possible "fear" that the for in construct can give. The for in, at least to me, strongly suggest pre-allocated array and not plain iteration. Forgetting one instant about the legacy for(;;)-from/to/by, if Swift had two for loop variants:

for element in array {} // Error if used with stride/range
for element iteratingOver iterator {} // Error if use with array (like one generated by filter()

It could help putting my irrational fear away. Yes, we can probably write the second as something like: iterationOver(iterator) { closure; } but then you cannot use break nor continue.

The example was in no-way a pro-from/to/by, as this format seem limited on multiple fronts, but it is the current pre-proposal format suggested by Ted based on MatLab syntax.


> 
>> Sorry if the example is a bit dumb as is; but I was too lazy to provide some nice code instead of the simple print.
>> Can anyone tell if the filter.prefix above is lazy and that each number is create one by one?
>> The fact that using 1_000_000 above caused my playground to crash, seems to indicate that the list of number is pre-generated.
> 
> Yes, I can tell you without looking at anything that the second example is greedy. `stride` acts lazy (it returns an instance which, when iterated over, produces the values), but `filter` is greedy unless you explicitly use `lazy`. If you don't know this, you can tell by looking at `filter`'s return type, which is an Array.
> 
> Your "explicit" syntax, on the other hand, seems totally out of control. Where do these features come from? What happens when you find something else you want to do in a loop? Do you invent another keyword and give it a new syntax? This *still* doesn't give you enough flexibility to express a `for` loop which doubles the number; is that going to require a new keyword? How many keywords is it going to take? Is anyone ever going to learn what all these keywords mean? Are you going to have to re-read The Swift Programming Language to decode every moderately complicated `for` loop?

So many questions for a single line of code. I do not think, i have invented any keyword, the from/to/by  have been part of the pre-proposal for a while. The where can already be used with the for in. The while already exist but is not associated with the for in. So by my count, I introduced 0 new keyword, and 1 new syntax: allowing while to be tagged against a for.

Putting the ‘while’ as part of my example was to cover a common case of for(;;) which came to mind where people use two conditions: an array upper bound, and an early exit (usually somethingFound). In C, the for (;;) is way too flexible and we certainly do not want that full flexibility in Swift loop structure, but I do believe that the for/in/while can provide clarity of the code intent than a for/in/if/break cannot.

> 
> (And how am I supposed to mentally parse your sea of undifferentiated alphanumerics? Punctuation in a programming language helps you understand the role of each part of it; your proposal's lack of punctuation makes it difficult to read.)

That’s true, there are not that many multiple keyword statement in Swift. Only one coming to mind is the: guard let x = o else {}

At least I didn’t go with textual operator:

for i from 2 to 1_000_000 by 1 where i modulo 2 notEqualTo 0 while foundCount lessThan 5 { print(i); foundCount incrementedBy 1 }

Dany

> The function/method-based approach requires a little more syntax and a little more understanding of what's going on. But has huge advantages that I think you're unfairly ignoring:
> 
> * It is simple. The `for` loop itself is quite uncomplicated. The only extra features it has are the `case` clause, which can't be easily duplicated in another way, and the `where` clause, which I've honestly never been totally happy with.
> * It is reusable. Calls like `stride`, `filter` and `prefix` can be used even outside a `for` loop.
> * It is extensible. If you need something new, you can add it, and it will be just as accessible as anything the standard library's designers dreamed up.
> * It is documentable. By using functions and methods instead of keywords, it's easier to look up what a particular feature does.
> * It is sustainable. If we have some kind of really specific looping mechanism like you propose, every little feature we want will need a specific design, evolution proposal, and implementation by the compiler team. That is a completely unrealistic plan. Not even the C `for` loop took this approach—it provided a *general* mechanism for plugging your own logic into a loop.
> 
> Ultimately, piling on new keywords every time you think of a tiny variation is simply bad language design. Taking this approach would be foolhardy.
> 
> -- 
> Brent Royal-Gordon
> Architechies
> 



More information about the swift-evolution mailing list