Straightforward Code Repetition

i mean i think most of us just learn to use ..< as the default (which sucks because it’s way uglier than ...). i would agree the _ is a problem but it would make more sense to just omit the _ in part so it would read

for 0 ..< 5 
{
    app.buttons["Add 30 Puzzles"].tap()
}

which would make more sense in the context of for loops

5 Likes

So it's not perfect. Not being able to break (continue can be emulated by doing an early return). If you need a simple "do this x times" loop, that probably isn't a big deal.

It seems like the primary motivation for this loop construct would be for teaching and progressive disclosure purposes, so it would undermine that if it was a control flow structure that behaved completely differently from all the other ones built into the language. It doesn't seem worthwhile at all if you now have to explain the difference between return and continue and why break doesn't work in some loop constructs. At that point it would be simpler to just teach the for _ in 1...n loop from the start.

1 Like

I didn't like this at first because using range syntax when you don't know where you are in the range seems a bit odd, but it does mean you can do...

for 1...5
{
    app.buttons["Add 30 Puzzles"].tap()
}

...which clears up the ..< ugliness!

1 Like

Agreed. Because forEach() has the same issue. It would be nice if these two loop idioms were more consistent. Personally, I favor the idea of having the language be flexible enough to code idioms like this at the library level, rather than having to add more syntax edge cases and sugar.

This is pretty off topic, I suspect, but I find it annoying that we have for and forEach and they do almost the same thing. I dislike how forEach looks, personally. I think it'd be better spelled as foreach. Considering that for is no longer a C-like for, it probably should have been renamed to foreach with the sequence function also renamed to foreach(). Then everything would be nice and pretty. And if there was a way to break and continue etc. out of closures, that'd be even more awesome.

It seems like if you are using a non escaping literal closure, break and continue and throw and return and whatever else should all pass through - maybe if spelled as a "force" like break! and continue!, etc.

The current Swift for loop can trace its roots back to Algol (which predates C, btw). There's nothing wrong with the name.

Yes it is a fact the name for has a long tradition and I'm well aware of that. It being a good name or not for Swift in 2018 is a matter of opinion. Is Swift likely to change this? No, probably not, but that doesn't invalidate my opinion that it could have had a better name nor does it validate your opinion that there's nothing wrong with it. We're all just talking here.

Tradition, by itself, isn't really a justification. "for has roots in Algol" is relevant if you are already familiar with programming. It's is completely irrelevant if you are new. Once you understand the idea of a loop, learning that it has a different name here or there is not a terrible hurdle.

On top of this, we could do something like the snippet below.
Someone objected to the Ruby syntax, which looks like this, but it also involves blocks and the ability to break from a block.

This solution is slightly different because it's still using a for loop, which allows break and continue.

Cons:

  • It doesn't look nice for negative numbers (I'm not sure why we would want it, but it comes for free with Int);
  • There could be a little friction to learn the property call in a number: 5.times.
extension Int {
    var times: Range<Int> {
        return times()
    }

    func times(bound: Int = 0) -> Range<Int> { // argument needs a better name
        if self < bound {
            return self..<bound
        }

        return bound..<self
    }
}

for 10.times {
    // do something
}

// Although it would also allow this
for i in (-5).times {
    print(i)
}

Why bother with the func there? If you actually need to use bounded version, then surely either the range operators or stride is simpler.

That times extension looks nice in the for... syntax but doesn't have much use elsewhere and would look odd anywhere else when it shows up in code completion.

A possible advantage of language constructs like

repeat N { ... }
repeat { ... } for N

over

N.times { ... }

would be that the former could be extended to “infinite repetition”

repeat { ... }

for loops which break somewhere inside the body. For example

repeat {
    let data = ifile.readData(ofLength: bufferSize)
    if data.count == 0 { break }
    ofile.write(data)
}

instead of while true { ... }, or (abusing?) pattern matching:

while case let data = ifile.readData(ofLength: bufferSize), data.count > 0 {
    ofile.write(data)
}
4 Likes

Perhaps to prevent accidental infinite loops, that case could be written as:

repeat _ {}

Yes, I think having some sort of marker would be better for the parser, instead of making the trailing while/for optional. (For instance, what would happen if a tailless repeat is followed by a distinct direct- while/ for?)

I know that this is a rather old thread but I wanted to document my experience. I've been teaching Swift to high school students for several years now. I find that it's both very readable for beginners and feature-rich for experts, and as such, suitable for a full three-year curriculum. While I spend time discussing syntactic sugar I think it's important for students to understand, at a deep level, exactly how things work.

While it's true that I've had some students initially struggle with the underscore it doesn't take much to fully comprehend it's purpose. Also, because:
for x in 1...3
is fundamentally the same as:
for _ in 1...3
with the exception of "caring" about the value of the loop control variable, I believe the existing solution is both sufficient and consistent. I prefer that my students learn how to address off-by-one errors through thorough study of ranges.

Finally, as the underscore appears in other contexts, it's useful to learn and understand in any case.

5 Likes

I'm still a fan of repeat 3 { ... } for counted loops. The possible addition of this was one of the reasons the do/while loop got renamed to repeat/while. This is highly precedented in Logo and other teaching languages, and would be great in the Swift Playgrounds iPad app.

It is a small thing, but it would fit in well.

-Chris

8 Likes

To help the things make sense for beginners, I would think some consistency would help, with the principle being: use for when you want the index at each count, and repeat when you don't.

This would lead to the normal form being like repeat 1...5 { .. }, comparable to for n in 1...5 { .. }.

I'd too like to see var times: Range<Int> added to give the easy to read repeat 5.times { .. } (although maybe less straightforward to teach beginners because they'd ask: why the dot?).

(However, I'd strongly say no to func times(bound: Int = 0) suggested by rguerreiro above, especially if it led to anything but an empty range for (-5).times. And while the function call 5.times() makes sense to return 0..<5, the call 5.times(3) reads like it should be a multiplication returning 15, not the range 3..<5)

And Swift has lots of shorter ways of saying the same thing (looking at you closures) so yeah, why not the shorter version repeat 5 { .. }. It doesn't need a correspondence with the for loop because it's not as clear what for n in 5 would mean.

I think for n in 5 would be 0? There are no “n”’s in “five”. :upside_down_face:

Terms of Service

Privacy Policy

Cookie Policy