Benefits of else clauses on while loops

There was an attempt to address this here, but it doesn't cover bindings and where clauses.


for x in 1...3 where x > 3 {
  print(x) // this is NOT executed
} else {
  print("not found") // this is executed
}

While I don't think else is clear enough naming there (I might expect else to run for all x where x <= 3, or for it to behave like Python, which does something different still), getting what you're asking for is truly gnarly:

`forElse`: do {
  var iterator = (1...3).makeIterator()
  var next: Int? {
    if let x = iterator.next(), x > 3
    { x } else { nil }
  }
  guard var x = next else {
    print("not found") // this is executed
    break `forElse`
  }
  `repeat`: do {
    print(x) // this is NOT executed
    guard let next else { break `repeat` }
    x = next
    continue `repeat`
  }
}

While this is an interesting solution, AFAICT it doesn't allow an arbitrary placement of break within the loop body, which is unfortunate.

Oh, that was just to illustrate it working. I would never use that nonsense.

1 Like

Here's the best I could come up with, still too ugly for real use:

@inline( __always )func pyWhile( body:()->Bool, welse:()->() = {})
{
      if !body()
      {
         welse()
      }
}

var x = 0
pyWhile{
   while x < 5
   {
      print( x )
      if( x >= 3 ) {return true}
      x += 1
   }
   return false
}
welse:
{
   print( "Normal exit" )
}

Note this code has to use "return" to break out of the body.

Output without return:

0
1
2
3
4
5
Normal exit

output with return:

0
1
2
3

I wonder if it would be possible to use Swift macros to implement the equivalent of a Python while...else statement? (Haven't had the time to dig deeply into Swift's macro capabilities, so I don't know).

After giving this subject way more thought that it really deserves, I think I've come up with the cleanest solution I can:

var x = 0
outahere:do
{
   while x < 5
   {
      print( x )
      //if( x >= 3 ) {break outahere}
      x += 1
   }
   print( "Normal exit" )
}

The only bit of syntactical ugliness is being forced to use a labeled break statement (well, plus an enclosing do statement).

The really nice thing about this approach is that it works for anything, not just while loops:

outahere:do
{
   for x in 0..<5
   {
      print( x )
      if( x >= 3 ) {break outahere}
   }
   print( "Normal exit" )
}

In fact, this scheme also solves a problem I've been complaining about elsewhere: you can use it to force a deinit call when the main program of a console application terminates (by wrapping all the outer-level code with a do{...}):

do
{

   class withDeinit
   {
      init()
      {
         print( "Creating class" )
      }
      
      deinit
      {
         print( "deinitializing class" )
      }
   }
   
   let classObject = withDeinit()
   

} // end do

Output from this last snippet is:

Creating class
deinitializing class

The trick is to wrap the entire outer level code with a do{...} statement and deinit() will be invoked when the do{...} statement goes out of scope.

I have to agree with Danny:

do 's power is underly advertised.

1 Like