for-else syntax


(Chris Davis) #1

Hi,

Often when I’m programming I stumble upon this scenario:

I have a list of items that may or may not be empty - if it’s full, I do one thing, if it’s empty I do something else, my code looks like this:

class Example_1
{
    let names = ["Chris", "John", "Jordan"]
    
    /// Loop over names, if no names, print no names
    func run()
    {
        for name in names
        {
            print(name)
        }
        
        if names.count == 0
        {
            print("no names")
        }
    }
}

let exampleOne = Example_1()
exampleOne.run()

However, Personally, I would find it more pleasing to write something like this:

class Example_2_Proposed
{
    let names:[String] = []
    
    /// Loop over names, if no names, print no names
    func run()
    {
        for name in names
        {
            print(name)
        } else {
            print("no names")
        }
    }
}

let exampleTwo = Example_2_Proposed()
exampleTwo.run()

The difference here is a “for-else” type syntax where if there were no items in the array it would simply fall through to the else statement.

What would be the pros/cons of introducing such syntax?

Is there’s a way of doing something similar in swift already?

Thanks

Chris


(Saagar Jha) #2

If you’re fine with a couple extra characters, you can use .isEmpty:

for name in names {
  // do your thing
}
if names.isEmpty {
  // do whatever
}

It’s a bit more typing, but I feel it makes your intentions more clear.

Saagar Jha

···

On Feb 1, 2017, at 8:48 AM, Chris Davis via swift-evolution <swift-evolution@swift.org> wrote:

Hi,

Often when I’m programming I stumble upon this scenario:

I have a list of items that may or may not be empty - if it’s full, I do one thing, if it’s empty I do something else, my code looks like this:

class Example_1
{
    let names = ["Chris", "John", "Jordan"]
    
    /// Loop over names, if no names, print no names
    func run()
    {
        for name in names
        {
            print(name)
        }
        
        if names.count == 0
        {
            print("no names")
        }
    }
}

let exampleOne = Example_1()
exampleOne.run()

However, Personally, I would find it more pleasing to write something like this:

class Example_2_Proposed
{
    let names:[String] = []
    
    /// Loop over names, if no names, print no names
    func run()
    {
        for name in names
        {
            print(name)
        } else {
            print("no names")
        }
    }
}

let exampleTwo = Example_2_Proposed()
exampleTwo.run()

The difference here is a “for-else” type syntax where if there were no items in the array it would simply fall through to the else statement.

What would be the pros/cons of introducing such syntax?

Is there’s a way of doing something similar in swift already?

Thanks

Chris

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


(Jacob Bandes-Storch) #3

One possible con: this is subtly but extremely different from Python, where
a for loop's else clause is executed only if there was no `break` from the
loop.

···

On Wed, Feb 1, 2017 at 8:48 AM Chris Davis via swift-evolution < swift-evolution@swift.org> wrote:

Hi,

Often when I’m programming I stumble upon this scenario:

I have a list of items that may or may not be empty - if it’s full, I do
one thing, if it’s empty I do something else, my code looks like this:

class Example_1
{
    let names = ["Chris", "John", "Jordan"]

    /// Loop over names, if no names, print no names
    func run()
    {
        for name in names
        {
            print(name)
        }

        if names.count == 0
        {
            print("no names")
        }
    }
}

let exampleOne = Example_1()
exampleOne.run()

However, Personally, I would find it more pleasing to write something like
this:

class Example_2_Proposed
{
    let names:[String] = []

    /// Loop over names, if no names, print no names
    func run()
    {
        for name in names
        {
            print(name)
        } else {
            print("no names")
        }
    }
}

let exampleTwo = Example_2_Proposed()
exampleTwo.run()

The difference here is a “for-else” type syntax where if there were no
items in the array it would simply fall through to the else statement.

What would be the pros/cons of introducing such syntax?

Is there’s a way of doing something similar in swift already?

Thanks

Chris

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


(Nicolas Fezans) #4

I tend to write this kind of treatment the other way around...

if names.isEmpty {
// do whatever
} // on other cases I might have a few else-if to treat other cases that
need special treament
else {
for name in names {
// do your thing
}
}

Nicolas Fezans

···

On Wed, Feb 1, 2017 at 6:31 PM, Saagar Jha via swift-evolution < swift-evolution@swift.org> wrote:

If you’re fine with a couple extra characters, you can use .isEmpty:

for name in names {
// do your thing
}
if names.isEmpty {
// do whatever
}

It’s a bit more typing, but I feel it makes your intentions more clear.

Saagar Jha

On Feb 1, 2017, at 8:48 AM, Chris Davis via swift-evolution < > swift-evolution@swift.org> wrote:

Hi,

Often when I’m programming I stumble upon this scenario:

I have a list of items that may or may not be empty - if it’s full, I do
one thing, if it’s empty I do something else, my code looks like this:

class Example_1
{
    let names = ["Chris", "John", "Jordan"]

    /// Loop over names, if no names, print no names
    func run()
    {
        for name in names
        {
            print(name)
        }

        if names.count == 0
        {
            print("no names")
        }
    }
}

let exampleOne = Example_1()
exampleOne.run()

However, Personally, I would find it more pleasing to write something like
this:

class Example_2_Proposed
{
    let names:[String] = []

    /// Loop over names, if no names, print no names
    func run()
    {
        for name in names
        {
            print(name)
        } else {
            print("no names")
        }
    }
}

let exampleTwo = Example_2_Proposed()
exampleTwo.run()

The difference here is a “for-else” type syntax where if there were no
items in the array it would simply fall through to the else statement.

What would be the pros/cons of introducing such syntax?

Is there’s a way of doing something similar in swift already?

Thanks

Chris

_______________________________________________
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


(Anton Zhilin) #5

You can write a helper method that adds a placeholder if the array is empty:

    for name in names.placeholder("no names") {
        print(name)
    }

Implementation:

    extension Collection {
        func placeholder(_ elem: Iterator.Element) -> PlaceholderView<Self>
{
            return PlaceholderView(collection: self, ifEmpty: elem)
        }
    }

    enum PlaceholderView<C: Collection> : Collection {
        case original(C)
        case placeholder(C.Iterator.Element)

        init(collection: C, ifEmpty: C.Iterator.Element) {
            if (collection.isEmpty) {
                self = .placeholder(ifEmpty)
            } else {
                self = .original(collection)
            }
        }

        // implement Collection conformance
    }

···

2017-02-01 19:48 GMT+03:00 Chris Davis via swift-evolution < swift-evolution@swift.org>:

Hi,

Often when I’m programming I stumble upon this scenario:

I have a list of items that may or may not be empty - if it’s full, I do
one thing, if it’s empty I do something else, my code looks like this:

class Example_1
{
    let names = ["Chris", "John", "Jordan"]

    /// Loop over names, if no names, print no names
    func run()
    {
        for name in names
        {
            print(name)
        }

        if names.count == 0
        {
            print("no names")
        }
    }
}

let exampleOne = Example_1()
exampleOne.run()

However, Personally, I would find it more pleasing to write something like
this:

class Example_2_Proposed
{
    let names:[String] = []

    /// Loop over names, if no names, print no names
    func run()
    {
        for name in names
        {
            print(name)
        } else {
            print("no names")
        }
    }
}

let exampleTwo = Example_2_Proposed()
exampleTwo.run()

The difference here is a “for-else” type syntax where if there were no
items in the array it would simply fall through to the else statement.

What would be the pros/cons of introducing such syntax?

Is there’s a way of doing something similar in swift already?

Thanks

Chris

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


(Erica Sadun) #6

I've taken this over to Swift Users from Swift Evolution.

I think you'd want to handle the exceptional case first, as it provides an opportunity for early exit before processing normal behavior. In such case, consider using a guard rather than an if:

guard !names.isEmpty else { print("No names"; return }
for name in names { ... }

Flipping the two tests allows you to use an existing if-else without changing the language, although there's really nothing that the `else` is adding here:

if names.isEmpty { ... }
else for name in names { ... }

-- E

···

On Feb 1, 2017, at 10:31 AM, Saagar Jha via swift-evolution <swift-evolution@swift.org> wrote:

If you’re fine with a couple extra characters, you can use .isEmpty:

for name in names {
  // do your thing
}
if names.isEmpty {
  // do whatever
}

It’s a bit more typing, but I feel it makes your intentions more clear.

Saagar Jha

On Feb 1, 2017, at 8:48 AM, Chris Davis via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi,

Often when I’m programming I stumble upon this scenario:

I have a list of items that may or may not be empty - if it’s full, I do one thing, if it’s empty I do something else, my code looks like this:

class Example_1
{
    let names = ["Chris", "John", "Jordan"]
    
    /// Loop over names, if no names, print no names
    func run()
    {
        for name in names
        {
            print(name)
        }
        
        if names.count == 0
        {
            print("no names")
        }
    }
}

let exampleOne = Example_1()
exampleOne.run()

However, Personally, I would find it more pleasing to write something like this:

class Example_2_Proposed
{
    let names:[String] = []
    
    /// Loop over names, if no names, print no names
    func run()
    {
        for name in names
        {
            print(name)
        } else {
            print("no names")
        }
    }
}

let exampleTwo = Example_2_Proposed()
exampleTwo.run()

The difference here is a “for-else” type syntax where if there were no items in the array it would simply fall through to the else statement.

What would be the pros/cons of introducing such syntax?

Is there’s a way of doing something similar in swift already?

Thanks

Chris

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto: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


(Freak Show) #7

collection ifEmpty:[ "...." ] ifNotEmptyDo: [:each | "...." ]

···

On Feb 1, 2017, at 10:29, Chris Davis via swift-evolution <swift-evolution@swift.org> wrote:

ah! I forgot about the break semantics, that’s definitely one for the con list.

I like Nicolas’ solution, clear to read.

On 1 Feb 2017, at 18:18, Nicolas Fezans <nicolas.fezans@gmail.com <mailto:nicolas.fezans@gmail.com>> wrote:

I tend to write this kind of treatment the other way around...

if names.isEmpty {
  // do whatever
} // on other cases I might have a few else-if to treat other cases that need special treament
else {
  for name in names {
    // do your thing
  }
}

Nicolas Fezans

On Wed, Feb 1, 2017 at 6:31 PM, Saagar Jha via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
If you’re fine with a couple extra characters, you can use .isEmpty:

for name in names {
  // do your thing
}
if names.isEmpty {
  // do whatever
}

It’s a bit more typing, but I feel it makes your intentions more clear.

Saagar Jha

On Feb 1, 2017, at 8:48 AM, Chris Davis via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi,

Often when I’m programming I stumble upon this scenario:

I have a list of items that may or may not be empty - if it’s full, I do one thing, if it’s empty I do something else, my code looks like this:

class Example_1
{
    let names = ["Chris", "John", "Jordan"]
    
    /// Loop over names, if no names, print no names
    func run()
    {
        for name in names
        {
            print(name)
        }
        
        if names.count == 0
        {
            print("no names")
        }
    }
}

let exampleOne = Example_1()
exampleOne.run()

However, Personally, I would find it more pleasing to write something like this:

class Example_2_Proposed
{
    let names:[String] = []
    
    /// Loop over names, if no names, print no names
    func run()
    {
        for name in names
        {
            print(name)
        } else {
            print("no names")
        }
    }
}

let exampleTwo = Example_2_Proposed()
exampleTwo.run()

The difference here is a “for-else” type syntax where if there were no items in the array it would simply fall through to the else statement.

What would be the pros/cons of introducing such syntax?

Is there’s a way of doing something similar in swift already?

Thanks

Chris

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

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto: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


(Sean Heber) #8

I usually use guard (or sometimes if) and an early return:

func run() {
  guard !names.isEmpty else {
    /* stuff */
    return
  }
  
  /* stuff with names */
}

l8r
Sean

···

On Feb 1, 2017, at 12:18 PM, Nicolas Fezans via swift-evolution <swift-evolution@swift.org> wrote:

I tend to write this kind of treatment the other way around...

if names.isEmpty {
  // do whatever
} // on other cases I might have a few else-if to treat other cases that need special treament
else {
  for name in names {
    // do your thing
  }
}

Nicolas Fezans

On Wed, Feb 1, 2017 at 6:31 PM, Saagar Jha via swift-evolution <swift-evolution@swift.org> wrote:
If you’re fine with a couple extra characters, you can use .isEmpty:

for name in names {
  // do your thing
}
if names.isEmpty {
  // do whatever
}

It’s a bit more typing, but I feel it makes your intentions more clear.

Saagar Jha

On Feb 1, 2017, at 8:48 AM, Chris Davis via swift-evolution <swift-evolution@swift.org> wrote:

Hi,

Often when I’m programming I stumble upon this scenario:

I have a list of items that may or may not be empty - if it’s full, I do one thing, if it’s empty I do something else, my code looks like this:

class Example_1
{
    let names = ["Chris", "John", "Jordan"]
    
    /// Loop over names, if no names, print no names
    func run()
    {
        for name in names
        {
            print(name)
        }
        
        if names.count == 0
        {
            print("no names")
        }
    }
}

let exampleOne = Example_1()
exampleOne.run()

However, Personally, I would find it more pleasing to write something like this:

class Example_2_Proposed
{
    let names:[String] = []
    
    /// Loop over names, if no names, print no names
    func run()
    {
        for name in names
        {
            print(name)
        } else {
            print("no names")
        }
    }
}

let exampleTwo = Example_2_Proposed()
exampleTwo.run()

The difference here is a “for-else” type syntax where if there were no items in the array it would simply fall through to the else statement.

What would be the pros/cons of introducing such syntax?

Is there’s a way of doing something similar in swift already?

Thanks

Chris

_______________________________________________
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

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


(Dimitri Racordon) #9

I agree. A for-else loop with different semantics than python would be error-prone for many people.

···

On 1 Feb 2017, at 18:17, Jacob Bandes-Storch via swift-evolution <swift-evolution@swift.org<mailto:swift-evolution@swift.org>> wrote:

One possible con: this is subtly but extremely different from Python, where a for loop's else clause is executed only if there was no `break` from the loop.
On Wed, Feb 1, 2017 at 8:48 AM Chris Davis via swift-evolution <swift-evolution@swift.org<mailto:swift-evolution@swift.org>> wrote:
Hi,

Often when I’m programming I stumble upon this scenario:

I have a list of items that may or may not be empty - if it’s full, I do one thing, if it’s empty I do something else, my code looks like this:

class Example_1
{
    let names = ["Chris", "John", "Jordan"]

    /// Loop over names, if no names, print no names
    func run()
    {
        for name in names
        {
            print(name)
        }

        if names.count == 0
        {
            print("no names")
        }
    }
}

let exampleOne = Example_1()
exampleOne.run()

However, Personally, I would find it more pleasing to write something like this:

class Example_2_Proposed
{
    let names:[String] = []

    /// Loop over names, if no names, print no names
    func run()
    {
        for name in names
        {
            print(name)
        } else {
            print("no names")
        }
    }
}

let exampleTwo = Example_2_Proposed()
exampleTwo.run()

The difference here is a “for-else” type syntax where if there were no items in the array it would simply fall through to the else statement.

What would be the pros/cons of introducing such syntax?

Is there’s a way of doing something similar in swift already?

Thanks

Chris

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


(Chris Davis) #10

ah! I forgot about the break semantics, that’s definitely one for the con list.

I like Nicolas’ solution, clear to read.

···

On 1 Feb 2017, at 18:18, Nicolas Fezans <nicolas.fezans@gmail.com> wrote:

I tend to write this kind of treatment the other way around...

if names.isEmpty {
  // do whatever
} // on other cases I might have a few else-if to treat other cases that need special treament
else {
  for name in names {
    // do your thing
  }
}

Nicolas Fezans

On Wed, Feb 1, 2017 at 6:31 PM, Saagar Jha via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
If you’re fine with a couple extra characters, you can use .isEmpty:

for name in names {
  // do your thing
}
if names.isEmpty {
  // do whatever
}

It’s a bit more typing, but I feel it makes your intentions more clear.

Saagar Jha

On Feb 1, 2017, at 8:48 AM, Chris Davis via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi,

Often when I’m programming I stumble upon this scenario:

I have a list of items that may or may not be empty - if it’s full, I do one thing, if it’s empty I do something else, my code looks like this:

class Example_1
{
    let names = ["Chris", "John", "Jordan"]
    
    /// Loop over names, if no names, print no names
    func run()
    {
        for name in names
        {
            print(name)
        }
        
        if names.count == 0
        {
            print("no names")
        }
    }
}

let exampleOne = Example_1()
exampleOne.run()

However, Personally, I would find it more pleasing to write something like this:

class Example_2_Proposed
{
    let names:[String] = []
    
    /// Loop over names, if no names, print no names
    func run()
    {
        for name in names
        {
            print(name)
        } else {
            print("no names")
        }
    }
}

let exampleTwo = Example_2_Proposed()
exampleTwo.run()

The difference here is a “for-else” type syntax where if there were no items in the array it would simply fall through to the else statement.

What would be the pros/cons of introducing such syntax?

Is there’s a way of doing something similar in swift already?

Thanks

Chris

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

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


(Charlie Monroe) #11

+1

This is generally why I've suggested about a week or two ago "NonEmptyArray" - an array that ensures it's not empty. Which is IMHO a sensible thing to use sometimes instead...

···

On Feb 1, 2017, at 8:38 PM, Sean Heber via swift-evolution <swift-evolution@swift.org> wrote:

I usually use guard (or sometimes if) and an early return:

func run() {
guard !names.isEmpty else {
   /* stuff */
   return
}

/* stuff with names */
}

l8r
Sean

On Feb 1, 2017, at 12:18 PM, Nicolas Fezans via swift-evolution <swift-evolution@swift.org> wrote:

I tend to write this kind of treatment the other way around...

if names.isEmpty {
  // do whatever
} // on other cases I might have a few else-if to treat other cases that need special treament
else {
  for name in names {
    // do your thing
  }
}

Nicolas Fezans

On Wed, Feb 1, 2017 at 6:31 PM, Saagar Jha via swift-evolution <swift-evolution@swift.org> wrote:
If you’re fine with a couple extra characters, you can use .isEmpty:

for name in names {
  // do your thing
}
if names.isEmpty {
  // do whatever
}

It’s a bit more typing, but I feel it makes your intentions more clear.

Saagar Jha

On Feb 1, 2017, at 8:48 AM, Chris Davis via swift-evolution <swift-evolution@swift.org> wrote:

Hi,

Often when I’m programming I stumble upon this scenario:

I have a list of items that may or may not be empty - if it’s full, I do one thing, if it’s empty I do something else, my code looks like this:

class Example_1
{
   let names = ["Chris", "John", "Jordan"]

   /// Loop over names, if no names, print no names
   func run()
   {
       for name in names
       {
           print(name)
       }

       if names.count == 0
       {
           print("no names")
       }
   }
}

let exampleOne = Example_1()
exampleOne.run()

However, Personally, I would find it more pleasing to write something like this:

class Example_2_Proposed
{
   let names:[String] = []

   /// Loop over names, if no names, print no names
   func run()
   {
       for name in names
       {
           print(name)
       } else {
           print("no names")
       }
   }
}

let exampleTwo = Example_2_Proposed()
exampleTwo.run()

The difference here is a “for-else” type syntax where if there were no items in the array it would simply fall through to the else statement.

What would be the pros/cons of introducing such syntax?

Is there’s a way of doing something similar in swift already?

Thanks

Chris

_______________________________________________
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

_______________________________________________
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


(Jeremy Pereira) #12

ah! I forgot about the break semantics, that’s definitely one for the con list.

I like Nicolas’ solution, clear to read.

I tend to write this kind of treatment the other way around...

if names.isEmpty {
  // do whatever
} // on other cases I might have a few else-if to treat other cases that need special treament
else {
  for name in names {
    // do your thing
  }
}

This only works if you know the size of the sequence before you start iterating it. You can, for example, iterate a lazy sequence and calculating its size before iterating it defeats the object.Thus for { … } else { … } where the else block only executes if the for block was never executed does have some utility.

However, I am not in favour adding it. The same functionality can be achieved by counting the number of iterations and testing the count afterwards (or by using a boolean). It takes a couple of extra lines of code and an extra variable, but I think that is a Good Thing. It’s more explicit and (as the Python example shows) there could be hidden subtleties that confuse people if for … else … is badly designed. Also, in many cases, I would argue that treating the zero element sequence differently to the n > 0 element sequence is a code smell. About the only use-case I can think of off the top of my head is UI presentation e.g. “your search didn’t return any results” instead of a blank page.

Talking of Python, Swift is not Python and the argument not to implement a feature because its semantics conflict with the semantics of a similar looking feature in another language is bogus. I don’t see the Python for … else being different (and having looked it up to see what you all were talking about, my opinion is that the Python for … else is a disaster) as being a legitimate con to this cleaner and more logical idea in Swift.

···

On 1 Feb 2017, at 18:29, Chris Davis via swift-evolution <swift-evolution@swift.org> wrote:

On 1 Feb 2017, at 18:18, Nicolas Fezans <nicolas.fezans@gmail.com> wrote:


(Jeremy Pereira) #13

Although it is the way I would do it for an array, sometimes you don’t know that a sequence is empty until after you have iterated it.

···

On 2 Feb 2017, at 20:12, Erica Sadun via swift-users <swift-users@swift.org> wrote:

I've taken this over to Swift Users from Swift Evolution.

I think you'd want to handle the exceptional case first,


(Kevin Nattinger) #14

I ran into this not too long ago. Something to handle this case would definitely be nice, but I agree we shouldn’t take python’s syntax with different semantics.

···

On Feb 1, 2017, at 9:30 AM, Dimitri Racordon via swift-evolution <swift-evolution@swift.org> wrote:

I agree. A for-else loop with different semantics than python would be error-prone for many people.

On 1 Feb 2017, at 18:17, Jacob Bandes-Storch via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

One possible con: this is subtly but extremely different from Python, where a for loop's else clause is executed only if there was no `break` from the loop.
On Wed, Feb 1, 2017 at 8:48 AM Chris Davis via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Hi,

Often when I’m programming I stumble upon this scenario:

I have a list of items that may or may not be empty - if it’s full, I do one thing, if it’s empty I do something else, my code looks like this:

class Example_1
{
    let names = ["Chris", "John", "Jordan"]
    
    /// Loop over names, if no names, print no names
    func run()
    {
        for name in names
        {
            print(name)
        }
        
        if names.count == 0
        {
            print("no names")
        }
    }
}

let exampleOne = Example_1()
exampleOne.run()

However, Personally, I would find it more pleasing to write something like this:

class Example_2_Proposed
{
    let names:[String] = []
    
    /// Loop over names, if no names, print no names
    func run()
    {
        for name in names
        {
            print(name)
        } else {
            print("no names")
        }
    }
}

let exampleTwo = Example_2_Proposed()
exampleTwo.run()

The difference here is a “for-else” type syntax where if there were no items in the array it would simply fall through to the else statement.

What would be the pros/cons of introducing such syntax?

Is there’s a way of doing something similar in swift already?

Thanks

Chris

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto: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


Another try at allowing optional iteration
(Haravikk) #15

I'm of two minds on this feature; I kind of support the idea of the construct, especially because there are some behind the scenes optimisations it can do, and it can look neater.
However, I'm not at all keen on the re-use of else; if there were a better keyword I might suppose that, for example "empty" or something like that, but nothing I can think of feels quite right.

I mean, when it comes down to it the "best" way to write the loop is like:

var it = names.makeIterator()
if let first = it.next() {
  print(first)
  while let current = it.next() { print(current) }
} else { print("no names") }

However this is a horrible thing to do in your own code, especially if the loop body is larger than one line, but is just fine if it's done behind the scenes for you (complete with unwrapping of the iterators if their type is known).

Which is why I kind of like the idea of having the construct itself; otherwise, like others, I use the less "correct" option like so (for sequences):

var empty = true
for name in names { print(name); empty = false }
if empty { print("no names") }

At which point I simply hope that the compiler optimises away the assignment (since it only actually does something on the first pass).

So yeah, I can see a use for it, but I'd prefer a construct other than for/else to do it; at the very least a different keyword, as there's the possibility we could also have a while/else as well and it would need to be very clear, which I don't feel that for/else is.

···

On 2 Feb 2017, at 11:06, Jeremy Pereira via swift-evolution <swift-evolution@swift.org> wrote:

On 1 Feb 2017, at 18:29, Chris Davis via swift-evolution <swift-evolution@swift.org> wrote:

ah! I forgot about the break semantics, that’s definitely one for the con list.

I like Nicolas’ solution, clear to read.

On 1 Feb 2017, at 18:18, Nicolas Fezans <nicolas.fezans@gmail.com> wrote:

I tend to write this kind of treatment the other way around...

if names.isEmpty {
  // do whatever
} // on other cases I might have a few else-if to treat other cases that need special treament
else {
  for name in names {
    // do your thing
  }
}

This only works if you know the size of the sequence before you start iterating it. You can, for example, iterate a lazy sequence and calculating its size before iterating it defeats the object.Thus for { … } else { … } where the else block only executes if the for block was never executed does have some utility.

However, I am not in favour adding it. The same functionality can be achieved by counting the number of iterations and testing the count afterwards (or by using a boolean). It takes a couple of extra lines of code and an extra variable, but I think that is a Good Thing. It’s more explicit and (as the Python example shows) there could be hidden subtleties that confuse people if for … else … is badly designed. Also, in many cases, I would argue that treating the zero element sequence differently to the n > 0 element sequence is a code smell. About the only use-case I can think of off the top of my head is UI presentation e.g. “your search didn’t return any results” instead of a blank page.

Talking of Python, Swift is not Python and the argument not to implement a feature because its semantics conflict with the semantics of a similar looking feature in another language is bogus. I don’t see the Python for … else being different (and having looked it up to see what you all were talking about, my opinion is that the Python for … else is a disaster) as being a legitimate con to this cleaner and more logical idea in Swift.

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


(Nicolas Fezans) #16

While I understand that there is a rationale behind "NonEmptyArray", I
don't think that this applies to the original question of Chris.
It seems that it is fully acceptable for the array to be empty in the
considered case, but only some different treatment shall happen in that
case.

I could easily imagine that situation when querying a database: you do not
want to specify that the query have no answer, but in one case you might
want to iterate through the items matching you query and do something with
them whereas in the other case you might want to notify the user that there
were no match.

Nicolas

···

On Wed, Feb 1, 2017 at 10:46 PM, Charlie Monroe <charlie@charliemonroe.net> wrote:

+1

This is generally why I've suggested about a week or two ago
"NonEmptyArray" - an array that ensures it's not empty. Which is IMHO a
sensible thing to use sometimes instead...

> On Feb 1, 2017, at 8:38 PM, Sean Heber via swift-evolution < > swift-evolution@swift.org> wrote:
>
> I usually use guard (or sometimes if) and an early return:
>
> func run() {
> guard !names.isEmpty else {
> /* stuff */
> return
> }
>
> /* stuff with names */
> }
>
> l8r
> Sean
>
>
>
>> On Feb 1, 2017, at 12:18 PM, Nicolas Fezans via swift-evolution < > swift-evolution@swift.org> wrote:
>>
>> I tend to write this kind of treatment the other way around...
>>
>> if names.isEmpty {
>> // do whatever
>> } // on other cases I might have a few else-if to treat other cases
that need special treament
>> else {
>> for name in names {
>> // do your thing
>> }
>> }
>>
>>
>> Nicolas Fezans
>>
>>
>>
>> On Wed, Feb 1, 2017 at 6:31 PM, Saagar Jha via swift-evolution < > swift-evolution@swift.org> wrote:
>> If you’re fine with a couple extra characters, you can use .isEmpty:
>>
>> for name in names {
>> // do your thing
>> }
>> if names.isEmpty {
>> // do whatever
>> }
>>
>> It’s a bit more typing, but I feel it makes your intentions more clear.
>>
>> Saagar Jha
>>
>>> On Feb 1, 2017, at 8:48 AM, Chris Davis via swift-evolution < > swift-evolution@swift.org> wrote:
>>>
>>> Hi,
>>>
>>> Often when I’m programming I stumble upon this scenario:
>>>
>>> I have a list of items that may or may not be empty - if it’s full, I
do one thing, if it’s empty I do something else, my code looks like this:
>>>
>>> class Example_1
>>> {
>>> let names = ["Chris", "John", "Jordan"]
>>>
>>> /// Loop over names, if no names, print no names
>>> func run()
>>> {
>>> for name in names
>>> {
>>> print(name)
>>> }
>>>
>>> if names.count == 0
>>> {
>>> print("no names")
>>> }
>>> }
>>> }
>>>
>>> let exampleOne = Example_1()
>>> exampleOne.run()
>>>
>>> However, Personally, I would find it more pleasing to write something
like this:
>>>
>>> class Example_2_Proposed
>>> {
>>> let names:[String] = []
>>>
>>> /// Loop over names, if no names, print no names
>>> func run()
>>> {
>>> for name in names
>>> {
>>> print(name)
>>> } else {
>>> print("no names")
>>> }
>>> }
>>> }
>>>
>>> let exampleTwo = Example_2_Proposed()
>>> exampleTwo.run()
>>>
>>> The difference here is a “for-else” type syntax where if there were no
items in the array it would simply fall through to the else statement.
>>>
>>> What would be the pros/cons of introducing such syntax?
>>>
>>> Is there’s a way of doing something similar in swift already?
>>>
>>> Thanks
>>>
>>> Chris
>>>
>>>
>>>
>>>
>>> _______________________________________________
>>> 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
>>
>>
>> _______________________________________________
>> 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


(Dimitri Racordon) #17

Talking of Python, Swift is not Python and the argument not to implement a feature because its semantics conflict with the semantics of a similar looking feature in another language is bogus. I don’t see the Python for … else being different (and having looked it up to see what you all were talking about, my opinion is that the Python for … else is a disaster) as being a legitimate con to this cleaner and more logical idea in Swift.

Swift is not Python, but Python programmers do program in Swift. Yeah they can look up the documentation and see that the semantics is not the same, but that is definitely error-prone.

While this kind of construct happen quite often, I am not for changing the syntax of a for-loop. To be honest, I don’t think that "for { .. } else { .. }” is so clear intuitive and I think that I would choose most if not all of the other constructions that have been proposed to express the OP’s problem.

Maybe we can add a new parameter "otherwise" to the forEach method

[1,2,3,4].forEach({
// do something
}
, otherwise: {
// do something if it is an empty array
})

The problem with this approach is that it would prevent the body of the for-loop to be expressed with a trailing closure.


(Howard Lovatt) #18

Why not add to the library:

extension Sequence {
    func forEach(_ eacher: (Iterator.Element) throws -> Void, elser: ()
throws -> Void) rethrows {
        var hasIterated = false
        for element in self {
            hasIterated = true
            try eacher(element)
        }
        guard hasIterated else {
            try elser()
            return
        }
    }
}

···

On Fri, 3 Feb 2017 at 8:33 pm, Jeremy Pereira via swift-users < swift-users@swift.org> wrote:

> On 2 Feb 2017, at 20:12, Erica Sadun via swift-users < > swift-users@swift.org> wrote:
>
> I've taken this over to Swift Users from Swift Evolution.
>
> I think you'd want to handle the exceptional case first,

Although it is the way I would do it for an array, sometimes you don’t
know that a sequence is empty until after you have iterated it.

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

--
-- Howard.


(Derrick Ho) #19

Maybe we can add a new parameter "otherwise" to the forEach method

[1,2,3,4].forEach({
// do something
}
, otherwise: {
// do something if it is an empty array
})

···

On Thu, Feb 2, 2017 at 6:31 AM Haravikk via swift-evolution < swift-evolution@swift.org> wrote:

I'm of two minds on this feature; I kind of support the idea of the
construct, especially because there are some behind the scenes
optimisations it can do, and it can look neater.
However, I'm not at all keen on the re-use of else; if there were a better
keyword I might suppose that, for example "empty" or something like that,
but nothing I can think of feels quite right.

I mean, when it comes down to it the "best" way to write the loop is like:

var it = names.makeIterator()
if let first = it.next() {
print(first)
while let current = it.next() { print(current) }

} else { print("no names") }

However this is a horrible thing to do in your own code, especially if the
loop body is larger than one line, but is just fine if it's done behind the
scenes for you (complete with unwrapping of the iterators if their type is
known).

Which is why I kind of like the idea of having the construct itself;
otherwise, like others, I use the less "correct" option like so (for
sequences):

var empty = true
for name in names { print(name); empty = false }
if empty { print("no names") }

At which point I simply hope that the compiler optimises away the
assignment (since it only actually does something on the first pass).

So yeah, I can see a use for it, but I'd prefer a construct other than
for/else to do it; at the very least a different keyword, as there's the
possibility we could also have a while/else as well and it would need to be
very clear, which I don't feel that for/else is.

On 2 Feb 2017, at 11:06, Jeremy Pereira via swift-evolution < > swift-evolution@swift.org> wrote:

On 1 Feb 2017, at 18:29, Chris Davis via swift-evolution < > swift-evolution@swift.org> wrote:

ah! I forgot about the break semantics, that’s definitely one for the con
list.

I like Nicolas’ solution, clear to read.

On 1 Feb 2017, at 18:18, Nicolas Fezans <nicolas.fezans@gmail.com> wrote:

I tend to write this kind of treatment the other way around...

if names.isEmpty {
// do whatever
} // on other cases I might have a few else-if to treat other cases that
need special treament
else {
for name in names {
// do your thing
}
}

This only works if you know the size of the sequence before you start
iterating it. You can, for example, iterate a lazy sequence and calculating
its size before iterating it defeats the object.Thus for { … } else { … }
where the else block only executes if the for block was never executed does
have some utility.

However, I am not in favour adding it. The same functionality can be
achieved by counting the number of iterations and testing the count
afterwards (or by using a boolean). It takes a couple of extra lines of
code and an extra variable, but I think that is a Good Thing. It’s more
explicit and (as the Python example shows) there could be hidden subtleties
that confuse people if for … else … is badly designed. Also, in many cases,
I would argue that treating the zero element sequence differently to the n
> 0 element sequence is a code smell. About the only use-case I can think
of off the top of my head is UI presentation e.g. “your search didn’t
return any results” instead of a blank page.

Talking of Python, Swift is not Python and the argument not to implement a
feature because its semantics conflict with the semantics of a similar
looking feature in another language is bogus. I don’t see the Python for …
else being different (and having looked it up to see what you all were
talking about, my opinion is that the Python for … else is a disaster) as
being a legitimate con to this cleaner and more logical idea in Swift.

_______________________________________________
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


(Jeremy Pereira) #20

Talking of Python, Swift is not Python and the argument not to implement a feature because its semantics conflict with the semantics of a similar looking feature in another language is bogus. I don’t see the Python for … else being different (and having looked it up to see what you all were talking about, my opinion is that the Python for … else is a disaster) as being a legitimate con to this cleaner and more logical idea in Swift.

Swift is not Python, but Python programmers do program in Swift. Yeah they can look up the documentation and see that the semantics is not the same, but that is definitely error-prone.

I have to say "so what”? You move from one language to another, there are points of confusion. There is no legitimate argument that says Swift should not implement a feature because it would confuse Python programmers (or programmers in any other language). If there were, Swift wouldn’t exist, we would just have a new Python-Coocoa bridge.

While this kind of construct happen quite often, I am not for changing the syntax of a for-loop. To be honest, I don’t think that "for { .. } else { .. }” is so clear intuitive and I think that I would choose most if not all of the other constructions that have been proposed to express the OP’s problem.

That is my position too. There are (persuasive IMO) arguments to say let’s not do this. My rant above was just to say “different to language X and therefore confusing to language X programmers” is not one of them.

···

On 3 Feb 2017, at 09:27, Dimitri Racordon via swift-evolution <swift-evolution@swift.org> wrote: