Post-Evaluating proposal SE-0007 Remove C-style for-loops

Well said, Brent!

With that I meant the arguments and not the expletive...
Just to be sure that I'm not misunderstood :-)

-Thorsten

···

Am 31.03.2016 um 15:34 schrieb Thorsten Seitz via swift-evolution <swift-evolution@swift.org>:

-Thorsten

Am 23.03.2016 um 02:25 schrieb Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org>:

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.

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?

(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.)

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

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Speaking of where, why wouldn’t you just do:

  for eachElement in theIntegerSequence where eachElement < 5 { … }

I think simplified striding is really all we need to cover c-style loop replacement, the only case that can’t be covered is one that uses some kind of variable stride amount, i.e- replacing:

  var step = 0
  for (var i = 0; i < 100; i += step) { step += 1 }

But I think that’s unusual enough that it doesn’t really matter.

···

On 23 Mar 2016, at 14:55, Jeremy Pereira via swift-evolution <swift-evolution@swift.org> wrote:

On 23 Mar 2016, at 11:38, Brent Royal-Gordon <brent@architechies.com> wrote:

One advantage of the old C style for loop is that everything to do with loop control is in one place, usually on one line. There is currently no way of doing that for the (quite common) use case of iterating through a sequence until a particular condition (other than the end of the sequence) is true except by using a break.

If you can stand using method chains, I believe that role would be filled by the `takeWhile(_:)` method that Kevin Ballard (IIRC) wants to add to Sequence. (Although `takeWhile(_:)` would be greedy by default.)

After writing the last email, I tried adding a method to SequenceType called whileTrue(_:) that did pretty much the same thing. It wrapped a Generator in another custom Generator that ended when the supplied closure returned true and it worked fine. However, the closure had no visibility of the iterator so

   for i in someIntegerSequence.whileTrue({ i < 5 }) { … }

was understandably a compiler error.

But honestly, other than distaste, I don't see much of a practical issue with putting an `if` or `guard` on the first line with a `break` in it. That still clusters the iteration logic at the top of the loop, even if it's not quite in a single statement.

True, but we could have argued the same about the `where` keyword and that is in existence. :-)

I get this

var i:Int = 0
for i in (0..<len)
{
    //do something
}

....

for i in (0..<len)
{
    if something is true
    {
        break
    }
}

use i ??
What is i now :) !!!!

The outer `i` is 0 because it was never used. The expression `for i in...` implicitly declares a new `i`.

···

--
Brent Royal-Gordon
Architechies

But honestly, other than distaste, I don't see much of a practical issue with putting an `if` or `guard` on the first line with a `break` in it. That still clusters the iteration logic at the top of the loop, even if it's not quite in a single statement.

True, but we could have argued the same about the `where` keyword and that is in existence. :-)

I believe I've spoken against the `where` clause earlier in this thread, but if I didn't, I will now: I don't like it. However, I believe it was added at the same time as `case` in the variable slot, and essentially as part of the same package of features, so I at least understand where it's coming from.

···

--
Brent Royal-Gordon
Architechies

Speaking of where, why wouldn’t you just do:

  for eachElement in theIntegerSequence where eachElement < 5 { … }

The `while` clause in the example we discussed was testing a separate variable which was being used to count the number of matches. Thus, it was playing the role of `prefix` (but in a less understandable way).

···

--
Brent Royal-Gordon
Architechies

I get this

var i:Int = 0
for i in (0..<len)
{
//do something
}

....

for i in (0..<len)
{
if something is true
{
break
}
}

use i ??
What is i now :) !!!!

Yes and it is extremly confusing sintax..... and you may create such bug without even notice just by changing the old C style for cycles.
I am still new to SWIFT (2 months). Here is what I thing
1. Breaking existing code meens that swift is still in beta and one should always think twice before investing in big project.
2. swift has no clear vision of "for" cycles. All cycles resemble "while" cycle when compiled to CPU instructions and can be implemented with while loop. Pascal had a limited for cycles just for easy enumeration. C on the other hand has much more capable for cycles that suited many cases. From what I read there is no clear idea about "for" in swift. They have to write down the scenarios they want to cover and and offer ONE syntax for them all. Having a different syntax for different C scenarios is confusing - better leave the C syntax.
3. swift has hidden function calls like C# - that affects the code performance in an unexpected way. Function calls are expensive in ARM architecture and they should be avoided in loops. So this leads to the arrays. Swift is slow beacause reading and changing array members is slow and that is what usually is made in loops. There should be no hiden function calls there. I may suggest using UnsafeMuttablePointer to create a new fast array class or struct and add it into the language for everyone to use.
4. writing many things in one line is BAD in million lines projects. It is called "compressed logic" - leads to bugs, and slow code maintenance. Swift closers are becoming really messy at this point so some rules against compressed logic are needed.
5. There are many great things in swift that I have not seen anywhere else like swift functions, interaction with C code, dictionaries and more and more. So I have a strong believe in the swift development team .

···

On Thursday, March 24, 2016 5:59 AM, Brent Royal-Gordon <brent@architechies.com> wrote:

> I get this

var i:Int = 0
for i in (0..<len)
{
//do something
}

....

for i in (0..<len)
{
if something is true
{
break
}
}

use i ??
What is i now :) !!!!

The outer `i` is 0 because it was never used. The expression `for i in...` implicitly declares a new `i`.

--
Brent Royal-Gordon
Architechies

True, but we could have argued the same about the `where` keyword and that is in existence. :-)

Speaking of where, why wouldn’t you just do:

  for eachElement in theIntegerSequence where eachElement < 5 { … }

Apologies, I’m a bit late to the party here, but I’ve been away on holiday.

The problem with `where` is that the loop still consumes all the elements in the sequence even in the case where the where clause can never be true again. So the above is the same as

    for eachElement in theIntegerSequence
    {
        if eachElement < 5 { … }
    }

I think a more useful construct is one that is equivalent to

    for eachElement in theIntegerSequence
    {
        guard eachElement < 5 else { break }
        ...
    }

but as Brent pointed out, putting that guard statement at the top of the loop body pretty much answers my main criticism of not having something like for … while …

I think simplified striding is really all we need to cover c-style loop replacement, the only case that can’t be covered is one that uses some kind of variable stride amount, i.e- replacing:

  var step = 0
  for (var i = 0; i < 100; i += step) { step += 1 }

But I think that’s unusual enough that it doesn’t really matter.

And it is covered by using a while loop.

It makes me smile somewhat that people think Swift is unusual not having a C style for loop. When I learned C, it was the _only_ language with such a flexible for loop. All of the others (at least those that I came in contact with) only had the simple for i = x to y step z style for loop.

···

On 23 Mar 2016, at 15:50, Haravikk <swift-evolution@haravikk.me> wrote: