Extending declaration scope to condition for `repeat { } while ()`

Good morning, Swift community!

I’ve come across a situation a number of times where I write code that has to try something one or more times using a `repeat…while` loop and the condition relies upon variables that should be declared within the scope of the loop.

repeat
{
  let success = doSomething()
}
while !success

The compiler unnecessarily prohibits this: “Use of unresolved identifier four.” In this simple case, we can write:

repeat
{ }
while !doSomething()

But in a more complex situation, we are forced to write:

var success: Bool
repeat
{
  success = doSomething()
}
while !success

We could change this so that the declarations within the top level scope of the loop are accessible from the condition.

Thanks for reading my first post to the Swift discussion board!
—Braeden

I would love this behavior, I ran into a case just a week or two ago where I was wishing for this behavior.

That said, I’m not sure whether it would be possible / feasible for the compiler. And it’s additive so it will have to wait for after Swift 3 is released.

···

On Jul 18, 2016, at 10:52 AM, Braeden Profile via swift-evolution <swift-evolution@swift.org> wrote:

Good morning, Swift community!

I’ve come across a situation a number of times where I write code that has to try something one or more times using a `repeat…while` loop and the condition relies upon variables that should be declared within the scope of the loop.

repeat
{
  let success = doSomething()
}
while !success

The compiler unnecessarily prohibits this: “Use of unresolved identifier four.” In this simple case, we can write:

repeat
{ }
while !doSomething()

But in a more complex situation, we are forced to write:

var success: Bool
repeat
{
  success = doSomething()
}
while !success

We could change this so that the declarations within the top level scope of the loop are accessible from the condition.

Thanks for reading my first post to the Swift discussion board!
—Braeden
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I’ve wanted this myself, too, so I’m generally +1, although I’ve also wondered if maybe this syntax should be changed somehow. I’ve not put a lot of thought into it, and this perhaps has come up before, but I sort of wonder.. would it make more sense to get rid of the trailing “while” entirely?

Here’s what I’m thinking:

This repeats forever - infinite loop:

repeat {}

And to get out of the infinite loop, you’d just use an if or guard the same way you might in any other loop in some cases:

repeat {
  let success = doSomething()
  guard success else { break }
}

We could potentially even warn if you use a repeat {} without there being a break inside the body somewhere.

This way, we eliminate a special syntactical form (repeat/while has always felt weird in every brace-based language I’ve used) and just piggyback on the existing break/continue/guard/if mechanisms that are already there and common. Then we also don’t need to have a special “weird” rule where the scope of variables change magically for repeat/while.

l8r
Sean

···

On Jul 18, 2016, at 12:52 PM, Braeden Profile via swift-evolution <swift-evolution@swift.org> wrote:

Good morning, Swift community!

I’ve come across a situation a number of times where I write code that has to try something one or more times using a `repeat…while` loop and the condition relies upon variables that should be declared within the scope of the loop.

repeat
{
  let success = doSomething()
}
while !success

The compiler unnecessarily prohibits this: “Use of unresolved identifier four.” In this simple case, we can write:

repeat
{ }
while !doSomething()

But in a more complex situation, we are forced to write:

var success: Bool
repeat
{
  success = doSomething()
}
while !success

We could change this so that the declarations within the top level scope of the loop are accessible from the condition.

Thanks for reading my first post to the Swift discussion board!
—Braeden
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

A few quick points. Apologies for the brevity.

1. Please take a look at the stateful version of `sequence`
2. Additive, non-breaking proposals should be deferred until next month.

In regard to point 1, create a sequence that terminates on false.
So you can hack something up like this (of course, hopefully a little more elegant)

extension Bool {
    var optionalPresentation: Bool? { return self ? self : nil }
}

// a fake example of terminating check
func doSomething() -> Bool {
    return arc4random_uniform(10) < 9 // 10% chance of ending
}

for nextItem in Swift.sequence(
    state: doSomething().optionalPresentation,
    next: { _ in return doSomething().optionalPresentation }) {
        
        // use nextItem state here
        // body of loop
}

Best,

-- E

···

On Jul 18, 2016, at 11:52 AM, Braeden Profile via swift-evolution <swift-evolution@swift.org> wrote:

Good morning, Swift community!

I’ve come across a situation a number of times where I write code that has to try something one or more times using a `repeat…while` loop and the condition relies upon variables that should be declared within the scope of the loop.

repeat
{
  let success = doSomething()
}
while !success

The compiler unnecessarily prohibits this: “Use of unresolved identifier four.” In this simple case, we can write:

repeat
{ }
while !doSomething()

But in a more complex situation, we are forced to write:

var success: Bool
repeat
{
  success = doSomething()
}
while !success

We could change this so that the declarations within the top level scope of the loop are accessible from the condition.

Thanks for reading my first post to the Swift discussion board!
—Braeden
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Strong +1 from me, I actually ran into a bunch of cases like this recently, having to put a tracking variable outside the loop feels weird; it makes sense for a regular while loop, but I see no reason that repeat/while couldn't have values from the loop be in scope for its condition.

There is a very slim chance of breakage if a variable is shadowed, can we use that as an excuse to get this done in Swift 3? =D

···

On 18 Jul 2016, at 18:52, Braeden Profile via swift-evolution <swift-evolution@swift.org> wrote:

Good morning, Swift community!

I’ve come across a situation a number of times where I write code that has to try something one or more times using a `repeat…while` loop and the condition relies upon variables that should be declared within the scope of the loop.

repeat
{
  let success = doSomething()
}
while !success

The compiler unnecessarily prohibits this: “Use of unresolved identifier four.” In this simple case, we can write:

repeat
{ }
while !doSomething()

But in a more complex situation, we are forced to write:

var success: Bool
repeat
{
  success = doSomething()
}
while !success

We could change this so that the declarations within the top level scope of the loop are accessible from the condition.

Thanks for reading my first post to the Swift discussion board!
—Braeden
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

1 Like

This is definitively something very useful but it also introduces a strong asymmetry into Swift statements. In all control-flow statements, the condition is part of the outer scope. Or, to be more precise, its part of an intermediate scope between the outer and the inner scope (as you can declare variables in the condition which are invisible to the outer scope but visible to the inner scope). Your suggestion essentially moves the condition of repeat {} while () to the inner scope. I think that the more complex semantics is not worth the change.

Here is how I deal with it btw:

process: do {

   let success = …

   guard success else { continue process }
}

Again, goto FTW :p

T.

···

On 18 Jul 2016, at 19:52, Braeden Profile via swift-evolution <swift-evolution@swift.org> wrote:

Good morning, Swift community!

I’ve come across a situation a number of times where I write code that has to try something one or more times using a `repeat…while` loop and the condition relies upon variables that should be declared within the scope of the loop.

repeat
{
  let success = doSomething()
}
while !success

The compiler unnecessarily prohibits this: “Use of unresolved identifier four.” In this simple case, we can write:

repeat
{ }
while !doSomething()

But in a more complex situation, we are forced to write:

var success: Bool
repeat
{
  success = doSomething()
}
while !success

We could change this so that the declarations within the top level scope of the loop are accessible from the condition.

Thanks for reading my first post to the Swift discussion board!
—Braeden
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

+1. Write it up.

Garth

Another +1 here.

···

On Mon, Jul 18, 2016 at 15:08 Garth Snyder via swift-evolution < swift-evolution@swift.org> wrote:

+1. Write it up.

Garth

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

That's a very interesting alternative, especially with a warning when there's no break (or return), and it would be clearer about the scope. Actually now I think about it, the repeat/while is a little odd in Swift since the while condition doesn't require parenthesis, which is great for a regular while or if condition because the braces give it structure, but that's not quite the case with the repeat/while.

I'm fine either way, but this should be explored as well; guard in particular goes well this style of repeat syntax. More interestingly though is that this would actually allow us to write while loops as:

  repeat {
    guard someCondition() else { break }
    doSomething()
  }

while would essentially just become a shorthand.

···

On 18 Jul 2016, at 21:32, Sean Heber via swift-evolution <swift-evolution@swift.org> wrote:

I’ve wanted this myself, too, so I’m generally +1, although I’ve also wondered if maybe this syntax should be changed somehow. I’ve not put a lot of thought into it, and this perhaps has come up before, but I sort of wonder.. would it make more sense to get rid of the trailing “while” entirely?

Here’s what I’m thinking:

This repeats forever - infinite loop:

repeat {}

And to get out of the infinite loop, you’d just use an if or guard the same way you might in any other loop in some cases:

repeat {
let success = doSomething()
guard success else { break }
}

We could potentially even warn if you use a repeat {} without there being a break inside the body somewhere.

This way, we eliminate a special syntactical form (repeat/while has always felt weird in every brace-based language I’ve used) and just piggyback on the existing break/continue/guard/if mechanisms that are already there and common. Then we also don’t need to have a special “weird” rule where the scope of variables change magically for repeat/while.

l8r
Sean

Great point; an infinite loop syntax would make a lot of sense. It seems good for it to have its own syntax, considering you must have a `break` or `return` to escape it. `while true { }` provides the same functionality, though it seems a bit less direct in its purpose than `repeat { }` could.

···

On Jul 18, 2016, at 4:16 PM, Haravikk <swift-evolution@haravikk.me> wrote:

On 18 Jul 2016, at 21:32, Sean Heber via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I’ve wanted this myself, too, so I’m generally +1, although I’ve also wondered if maybe this syntax should be changed somehow. I’ve not put a lot of thought into it, and this perhaps has come up before, but I sort of wonder.. would it make more sense to get rid of the trailing “while” entirely?

Here’s what I’m thinking:

This repeats forever - infinite loop:

repeat {}

And to get out of the infinite loop, you’d just use an if or guard the same way you might in any other loop in some cases:

repeat {
let success = doSomething()
guard success else { break }
}

We could potentially even warn if you use a repeat {} without there being a break inside the body somewhere.

This way, we eliminate a special syntactical form (repeat/while has always felt weird in every brace-based language I’ve used) and just piggyback on the existing break/continue/guard/if mechanisms that are already there and common. Then we also don’t need to have a special “weird” rule where the scope of variables change magically for repeat/while.

l8r
Sean

That's a very interesting alternative, especially with a warning when there's no break (or return), and it would be clearer about the scope. Actually now I think about it, the repeat/while is a little odd in Swift since the while condition doesn't require parenthesis, which is great for a regular while or if condition because the braces give it structure, but that's not quite the case with the repeat/while.

I'm fine either way, but this should be explored as well; guard in particular goes well this style of repeat syntax. More interestingly though is that this would actually allow us to write while loops as:

  repeat {
    guard someCondition() else { break }
    doSomething()
  }

while would essentially just become a shorthand.