[swift-evolution] Add a while clause to for loops

Dany St-Amant dsa.mls at icloud.com
Wed Jun 8 20:34:01 CDT 2016


Le 8 juin 2016 à 19:05, Tim Vermeulen via swift-evolution <swift-evolution at swift.org> a écrit :

>> Since Swift strives to be an opinionated language without dialects, there shouldn't be more "choice" but rather one general solution, IMO.
> 
> I agree with you on this in general, but this proposal isn’t just about adding choices to the language. At least, that’s not the point. It doesn’t add any new functionality per se, but there might be value in making a common coding pattern more shorter and more intuitive. `guard` is a lot more powerful than `where` or `while` as discussed here, but such clauses can possibly make code more readable. You don’t have to agree with me that those clauses are actually more readable than using `guard`, but that’s the way I see it and in my opinion it’s about more than just having more options.

I do not think shortness is that much a valid criteria (sorry), but expressiveness of the intention matter. A 'guard' send a message of a condition to be protected from; 'where' carry a notion of conditional criteria; while 'while' suggest a on going state.

Having the choice between if/guard/where/while can be seen as confusing to newbie (which one should I use, why does this code use one over the other), but for an experimented developer it can help carry the intention behind the code better than a comment could.

Dany 

> In fact, if this proposal is accepted, one could consider `guard someCondition else { continue/break }` at the start of a for loop to be a code smell because a `where`/`while` clause could have been used instead. So using those clauses would then be the general solution to the problem, which is in line with what you said Swift strives to be.
> 
> To me this issue feels similar to adding `guard` despite already having `if` (if we disregard `guard let` for a second). Now you can write both `guard someCondition else { return }` and `if !someCondition { return }`, but I would consider the first one the general solution and the second one code smell. This pattern is so common that adding `guard` was justified.
> 
>> On Wed, Jun 8, 2016 at 1:58 PM, Tim Vermeulen<tvermeulen at me.com(mailto:tvermeulen at me.com)>wrote:
>>> That’s why I said “potentially less elegant”, some people might prefer `where` over `guard`. This proposal would give them the choice (in very specific situations) to use `where` rather than `guard` if they don’t want to sacrifice performance.
>> Since Swift strives to be an opinionated language without dialects, there shouldn't be more "choice" but rather one general solution, IMO. Since `guard` doesn't sacrifice performance and is the most general, I would oppose adding the option of `while` to offer more choice.
>> 
>>> 
>>>> On Wed, Jun 8, 2016 at 1:35 PM, Tim Vermeulen via swift-evolution<swift-evolution at swift.org(mailto:swift-evolution at swift.org)(mailto:swift-evolution at swift.org)>wrote:
>>>>> This is a really strong argument in my opinion. If we don’t add a `while` to for loops, then in some situations we will have to rewrite a `where` clause to something potentially less elegant, given that we don’t want to give up performance.
>>>> I disagree. I argue that what you call "less elegant", namely if (or guard) inside the loop, is the most elegant solution.
>>>> 
>>>>> 
>>>>>> IMO `.prefix` is just not the equal alternative for as proposed `while` :
>>>>>> in case of 'while' expression `number<4_000_000` will be calculated
>>>>>> *only* for those who `number % 2 == 0`. In case of `prefix` - the
>>>>>> expression will be processed for each `number` and only after this filtered
>>>>>> by `number % 2`. Let's assume we need to check for some
>>>>>> veryExpensiveTest(number):
>>>>>> 
>>>>>> for number in fibonacci where number % 2 == 0 while
>>>>>> veryExpensiveTest(number) {}
>>>>>> 
>>>>>> let numbers = fibonacci.prefix { veryExpensiveTest($0) }
>>>>>> for number in numbers where number % 2 == 0 {}
>>>>>> 
>>>>>> So, `while` for `for` loops just can't be always replaced with `prefix`
>>>>>> 
>>>>>>> On 08.06.2016 2:02, Xiaodi Wu via swift-evolution wrote:
>>>>>>> On Tue, Jun 7, 2016 at 5:11 PM, Tim Vermeulen<tvermeulen at me.com(mailto:tvermeulen at me.com)(mailto:tvermeulen at me.com)
>>>>>>> <mailto:tvermeulen at me.com>>wrote:
>>>>>>> 
>>>>>>> I’ve been thinking about this for a bit now, and I think it would make
>>>>>>> most sense to evaluate these clauses from left to right. However, cases
>>>>>>> where the order matters are very uncommon, and I would rather have the
>>>>>>> power to choose which clause is evaluated first than to have a forced
>>>>>>> default order. Either way I don’t see this as a reason not to allow
>>>>>>> combining the two clauses because IMO it can lead to some very clean
>>>>>>> code. For instance, say we want to loop through all even fibonacci
>>>>>>> numbers below 4 million (see problem #2 from project euler), we could
>>>>>>> do this:
>>>>>>> 
>>>>>>> `for number in fibonacci where number % 2 == 0 while number<4_000_000
>>>>>>> { }`
>>>>>>> 
>>>>>>> 
>>>>>>> This statement looks like spaghetti to me. I would not at all support
>>>>>>> extending the language to permit it. Do you really think it's more readable
>>>>>>> than going step-by-step?
>>>>>>> 
>>>>>>> ```
>>>>>>> let numbers = fibonacci.prefix { $0<4_000_000 }
>>>>>>> for number in numbers where number % 2 == 0 {
>>>>>>> // ...
>>>>>>> }
>>>>>>> ```
>>>>>>> 
>>>>>>> or just:
>>>>>>> 
>>>>>>> ```
>>>>>>> let numbers = fibonacci.prefix { $0<4_000_000 }
>>>>>>> let evens = numbers.filter { $0 % 2 == 0 }
>>>>>>> for number in evens {
>>>>>>> // ...
>>>>>>> }
>>>>>>> ```
>>>>>>> 
>>>>>>> 
>>>>>>> I could have ordered the two clauses in any way I want. If combining
>>>>>>> the clauses weren’t allowed, I’d have to put (at least) one of them
>>>>>>> inside the block, which would be a (minor) pain.
>>>>>>> 
>>>>>>> I don’t currently have a very strong opinion about the order of
>>>>>>> evaluation, so I might be convinced otherwise. But combining the two
>>>>>>> clauses is so powerful that I don’t think it’s worth to get rid of just
>>>>>>> because of an edge case.
>>>>>>> 
>>>>>>>> It may be workable if you can have only one or the other, but mixing and matching them as proposed above would be a world of hurt:
>>>>>>>> 
>>>>>>>> ```
>>>>>>>> for foo in bar where condition1 while condition2 { ... }
>>>>>>>> ```
>>>>>>>> 
>>>>>>>> If condition1 and condition2 both evaluate to true, then whether you continue or break would depend on the relative order of where and while; for generality, you would want to allow both `for...in...where...while` and `for...in...while...where`, and likely `for...in...while...where...while`, etc. There is nothing in the meaning of those words that would suggest that `while...where` behaves differently from `where...while`, etc. This is why words like "break" and "continue" are IMO far superior.
>>>>>>>> 
>>>>>>>> 
>>>>>>>> On Tue, Jun 7, 2016 at 2:34 PM, Erica Sadun<erica at ericasadun.com(mailto:erica at ericasadun.com)(mailto:erica at ericasadun.com)
>>>>>>> <mailto:erica at ericasadun.com>(mailto:erica at ericasadun.com
>>>>>>> <mailto:erica at ericasadun.com>)>wrote:
>>>>>>>>> 
>>>>>>>>>> On Jun 7, 2016, at 1:16 PM, Tim Vermeulen via swift-evolution<swift-evolution at swift.org(mailto:swift-evolution at swift.org)(mailto:swift-evolution at swift.org)
>>>>>>> <mailto:swift-evolution at swift.org>(mailto:swift-evolution at swift.org
>>>>>>> <mailto:swift-evolution at swift.org>)>wrote:
>>>>>>>>>>> The meaning of the proposed while is not at all a pair for where, since where clauses in while loops would do the same thing as while clauses in for loops. That's crazy.
>>>>>>>>>> 
>>>>>>>>>> It sounds crazy, but it’s the nature of the while loop. A where clause in a while loop also has a different result than a where clause in a for loop.
>>>>>>>>> 
>>>>>>>>> The where_clause appears in the for in statement
>>>>>>>>> 
>>>>>>>>> for_in_statement : 'for' 'case'? pattern 'in' expression where_clause? code_block
>>>>>>>>> 
>>>>>>>>> It's syntactic sugar because the expression can be already be limited through functional chaining of some sort or another. At the same time, it's nice and pleasant to have `where` and I'm not itching to throw it out. The same courtesy could be easily extend to `when` (because I don't really want to use the `while` keyword here, but I could easily be convinced otherwise because I don't have a strong stance either way):
>>>>>>>>> 
>>>>>>>>> for_in_statement : 'for' 'case'? pattern 'in' expression (where_clause | when_clause)? code_block
>>>>>>>>> when_clause : 'when' expression
>>>>>>>>> 
>>>>>>>>> and again it could be nice and pleasant to have, although not necessary. The question comes down to how much does the language benefit by this sugar.
>>>>>>>>> 
>>>>>>>>> I'd say that in both cases, combining chaining and statements is
>>>>>>> marginallyless goodthan either using standalone chaining or statements
>>>>>>> without chaining. But as I say this, I know as a fact, I fully intend
>>>>>>> to use `sequence(_:, next:).take(while:)` with for0in statements, so
>>>>>>> I'm starting from a hypocritical vantage point.
>>>>>>>>> 
>>>>>>>>> To summarize, I'm more +0.01 than I am -0.01 on this.
>>>>>>>>> 
>>>>>>>>> -- E
>>>>>>>>> p.s. Sorry, wux
>>>>>>> 
>>>>>>> 
>>>>>>> 
>>>>>>> 
>>>>>>> _______________________________________________
>>>>>>> swift-evolution mailing list
>>>>>>> swift-evolution at swift.org(mailto:swift-evolution at swift.org)(mailto:swift-evolution at swift.org)
>>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>>> _______________________________________________
>>>>> swift-evolution mailing list
>>>>> swift-evolution at swift.org(mailto:swift-evolution at swift.org)(mailto:swift-evolution at swift.org)
>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution


More information about the swift-evolution mailing list