<div dir="ltr">On Sun, Jun 12, 2016 at 7:10 AM, plx via swift-evolution <span dir="ltr">&lt;<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>&gt;</span> wrote:<br><div class="gmail_extra"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div style="word-wrap:break-word"><br><div><blockquote type="cite"><div>On Jun 10, 2016, at 12:59 PM, Xiaodi Wu via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>&gt; wrote:</div><br><div><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On Fri, Jun 10, 2016 at 12:30 PM, let var go <span dir="ltr">&lt;<a href="mailto:letvargo@gmail.com" target="_blank">letvargo@gmail.com</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div dir="ltr"><div>I respect that anti-goal, but I think being over-rigid about limiting developers&#39; choice of expression is also an anti-goal.</div><div><br></div><div>To me, it is like guard statements vs. if-let statements. Some people find one to be more clear than the other. Often times the best choice depends on the context. Sometimes a guard statement can be re-written as an if-let statement in a way that makes the code more clear, and vice versa.</div></div></blockquote><div><br></div><div>The comparison with `guard` and `if` is a little inapt. The introduction of `guard` solved a practical daily issue with `if` that was nicknamed the pyramid of doom, where successive `if let` statements caused code to be severely nested in braces and nearly unreadable. Further, you must exist the scope with `guard`; thus, its use signals an intention not possible with `if`. If, on the other hand, you do not wish to exit the scope, you must use `if`. So in a Venn diagram, there are independent uses for `if` that cannot be fulfilled by `guard`, and uses for `guard` that would be unreadable if rewritten with `if`.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div dir="ltr"><div>And different people will inevitably have different personal preferences - their own &quot;style&quot;, if you will - and will favor one over the other. But it would be a mistake to force everyone into one box in order to prevent the fracturing of the Swift community into &quot;dialects.&quot;</div></div></blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div dir="ltr"><div><br></div><div>But most importantly (and this is really the kicker for me) there are times when the &quot;where&quot; syntax provides the maximum amount of clarity in the context of my code, and I don&#39;t want to lose that expressive power.<br></div></div></blockquote><div><br></div><div>This is the key and salient point here. Would you be able to share some examples where the `where` syntax provides a clear win in clarity? That would definitely be a huge pro, if it can be used to solve issues in expressiveness much like `guard` allowed elimination of the pyramid of doom.</div></div></div></div></div></blockquote><div><br></div><div>I’d hate to lose `where` in for-in loops; I use it a lot. My own 2c on it is that I think if you look at isolated uses of `where` it’s not adding much clarity, but having it can allow you to adopt more-consistent patterns overall…which IMHO *does* result in clearer code.</div><div><br></div><div>I’ll provide two examples of such “consistent patterns” and how the presence of `for`-`in` `where` helps stick with them.</div><div><br></div><div>First Pattern: for some simple types I like to do data-driven unit tests — here’s a collection of values to test, here’s a basic, “algebraic&quot; property that should apply, test it actually does, and so on.</div><div><br></div><div>When I write a set of tests like that, I like to have a single variable holding the “instances to test” that is shared by all relevant tests; this lets me start with only a handful of values, confirm it seems to work, and then have a single place to edit once I’m ready to expand the values we test upon.</div><div><br></div><div>Such tests might wind up looking like this:</div><div><br></div><div>  func testNaiveAncestor() {</div><div>     // the root has no ancestor so we simply to skip it:</div><div>     for position in testPositions where !position.isRootPosition {</div><div>        XCTAssertLessThan(</div><div>         position.naiveAncestor(), </div><div>         position</div><div>        )</div><div>     }</div><div>  }</div><div><br></div><div><div>  func testNaiveSuccessor() {</div><div>     for position in testPositions {</div><div>        XCTAssertGreaterThan(</div><div>         position.naiveSuccessor(), </div><div>         position</div><div>        )</div><div>     }</div><div>  }</div><div><br></div><div>…where `testPositions` holds the values to test, and the two tests are each testing a basic algebraic property. Having `where` available on for-loops makes it possible to write these in such a way that the intended parallelism between the two is visually-apparent—you can tell they’re the same sort of thing just by looking at them—and it does so in a way that makes few assumptions on the type-or-contents of `testPositions`. </div></div></div></div></blockquote><div><br></div><div>First, notice how your comment is related to the `where` clause but is actually sitting on top of the loop itself. Second, one of these two tests visits every element while the other doesn&#39;t, and it took me three reads before I could see that because I literally couldn&#39;t find the `where` clause the first two times I scanned through your code. This is a false &quot;parallelism,&quot; causing the very thing that *isn&#39;t* parallel to disappear from the reader&#39;s eye. Compare instead this alternative, which also flips the boolean assertion:</div><div><br></div><div>```</div><div>func testNaiveAncestor() {</div><div>  for position in testPositions {</div><div>    // the root has no ancestor</div><div>    <span style="background-color:rgb(255,255,0)">if position.isRootPosition { continue }</span></div><div><div>    // (notice how I don&#39;t even have to comment that we&#39;re skipping root,</div><div>    // because the code says it explicitly for me)</div></div><div><br></div><div>    XCTAssertLessThan(position.naiveAncestor(), position)</div><div>  }</div><div>}</div><div>```</div><div><br></div><div>Now, with my rewriting, the part of your test that is strictly similar to the other test looks parallel, and the one part that isn&#39;t at all similar (the skipping part) stands out explicitly and is now self-documenting code.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div style="word-wrap:break-word"><div><div><div><br></div><div>So `where` here, IMHO, isn’t *clearly* clearer in `testNaiveAncestor()`, but it lets `testNaiveAncestor()` and `testNaiveSuccessor()` (etc.) be *systemically*-clearer, as it were.</div><div><br></div><div>Second Pattern: relatedly, I find code is much clearer when `guard` is only-ever used for early exits and early returns. </div><div><br></div><div>There’s no requirement to *not* use `guard` and `continue` together, but if one is willing to stick to `guard` == “early exit / early return” it makes code much easier to read and audit.</div><div><br></div><div>In a handful of places I have for loops that would need both `continue`-style conditionals and also early-exit conditionals; having `where` means I can stick to using `guard` for early-exits, whereas without it I’d have extra nesting or mixed “early-exit” guard and “continue” guard. </div></div></div></div></blockquote><div><br></div><div>I don&#39;t know what to say here. The fact is that `guard` with `continue` is a documented and advertised feature of Swift; it&#39;s not simply supported by accident. Of course, you&#39;re free to choose not to use that feature at all. And it is true that, currently, `where` allows you to avoid that feature at the top of a `for` loop, but anywhere else inside the loop and you&#39;ll have to deal with extra nesting if you choose to reject `guard` with `continue`.</div><div><br></div><div>IIUC, one of the motivating reasons for introducing `guard` was to solve the pyramid of doom problem. So you&#39;re rejecting the intended solution for extra nesting, at least in certain circumstances, a choice you&#39;re obviously free to make in your own code. But when it comes to designing a language for everyone, the fact that some users reject the intended solution would be grounds for re-examining that solution (i.e. `guard`), but the mantra here has always been one solution where possible and not multiple. So this certainly cannot be a justification for another feature (i.e. `where`) which only incidentally provides a substitute solution in certain situations.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div style="word-wrap:break-word"><div><div><div></div><div>This is as short as I can make it:</div><div><br></div><div><div>  /// Returns the portion of our frame that *might* be visible on-screen; doesn&#39;t</div><div>  /// handle occlusion, but unlike `approximateWindowRegion` will account for </div><div>  /// clipping done by our superviews.</div><div>  ///</div><div>  /// - note: `intersectionRegion(with view: UIView)` takes `self.bounds`, converts</div><div>  ///         it to `view`&#39;s coordinates, intersects with `view.bounds`, then converts</div><div>  ///         that back to `self`&#39;s coordinates...returning `nil` for any empty rects.</div><div>  ///</div><div>  @warn_unused_result</div><div>  public final func approximateVisibleRegion() -&gt; CGRect? {</div><div>    // before checking everything, confirm we *have a window* and that our</div><div>    // position vis-a-vis the window could *possibly* make sense</div><div>    // no window =&gt; not visible</div><div>    guard let w = self.window else {</div><div>      return nil</div><div>    }</div><div>    // empty &quot;frame-intersect-window&quot; =&gt; not visible</div><div>    guard let regionInWindow = self.intersectionRegion(with: w) else {</div><div>      return nil</div><div>    }</div><div>    </div><div>    // now we prepare to &quot;walk upstream&quot;:</div><div>    var lastVisitedView = self</div><div>    var currentFrame = regionInWindow</div><div><br></div><div>    // walk &quot;upstream&quot; (starting from our superview), skipping:</div><div>    // - superviews that don&#39;t clip-to-bounds</div><div>    // - the window (since we already took our intersection with it)</div><div>    for upstreamView in self.exclusiveSuperviewSequence() where upstreamView.clipsToBounds &amp;&amp; upstreamView !== w {</div><div>      // finding a nil intersection =&gt; not visible, early-exit</div><div>      guard let upstreamIntersection = lastVisitedView.intersectionRegion(with: upstreamView) else {</div><div>        return nil</div><div>      }</div><div>      lastVisitedView = upstreamView</div><div>      currentFrame = upstreamIntersection</div><div>    }</div><div>    // belt-and-suspenders final steps:</div><div>    assert(!currentFrame.isEmpty &amp;&amp; !currentFrame.isNull)</div><div>    return self.convertRect(</div><div>      currentFrame, </div><div>      fromView: lastVisitedView</div><div>    ).onlyIfNonEmpty</div><div>  }</div></div><div><br></div><div>…and without `where` on `for`-`in` loops, the main `for` loop winds up looking like one of these:</div><div><br></div><div>    // with `if`:</div><div><div>    for upstreamView in self.exclusiveSuperviewSequence() {</div><div>      if upstreamView.clipsToBounds &amp;&amp; upstreamView !== w {</div><div>        // finding a nil intersection =&gt; not visible, early-exit</div><div>        guard let upstreamIntersection = lastVisitedView.intersectionRegion(with: upstreamView) else {</div><div>          return nil</div><div>        }</div><div>        lastVisitedView = upstreamView</div><div>        currentFrame = upstreamIntersection</div><div>      }</div><div>    }</div></div><div><br></div><div><div>    // with mixed-guard usage:</div><div><div>    for upstreamView in self.exclusiveSuperviewSequence() {</div><div>      guard upstreamView.clipsToBounds &amp;&amp; upstreamView !== w else {</div><div>        continue</div><div>      }</div><div>      // finding a nil intersection =&gt; not visible, early-exit</div><div>      guard let upstreamIntersection = lastVisitedView.intersectionRegion(with: upstreamView) else {</div><div>        return nil</div><div>      }</div><div>      lastVisitedView = upstreamView</div><div>      currentFrame = upstreamIntersection</div><div>      }</div><div>    }</div></div></div><div><br></div><div>…and again neither one is *awful*, but:</div><div><br></div><div>- the one with `if` adds another level of nesting (annoying!)</div><div>- the one with “guard” has mixed “guard” usage (continue/exit)</div><div><br></div><div>…and since I like to stick to early-exit guard—it makes it easier to read if “guard == exit method”—I’d have to go with the nesting option, which I just don’t like much.</div><div><br></div><div>Those are the strongest examples I can find; the rest are all essentially like this:</div><div><br></div><div>  extension Dictionary {</div></div><div><br></div><div>    // with `where`</div><div>    func mapValues&lt;T&gt;(excludingKeys keySet: Set&lt;Key&gt;, @noescape valueMap: (Value) -&gt; T) -&gt; [Key:T] {</div><div>      guard !keySet.isEmpty else { </div><div>        return self.mapValues(valueMap) </div><div>      }</div><div>      var result: [Key:T] = [:]</div><div>      for (key,value) in self where !keySet.contains(key) {</div><div>        result[key] = valueMap(result)</div><div>      }</div><div>      return result</div><div>    }</div><div><br></div><div>    // without `where`, `if`:</div><div><div>    func mapValues&lt;T&gt;(excludingKeys keySet: Set&lt;Key&gt;, @noescape valueMap: (Value) -&gt; T) -&gt; [Key:T] {</div><div>      guard !keySet.isEmpty else { </div><div>        return self.mapValues(valueMap) </div><div>      }</div><div>      var result: [Key:T] = [:]</div><div>      for (key,value) in self {</div><div>        if !keySet.contains(key) {</div><div>          result[key] = valueMap(result)</div><div>        }</div><div>      }</div><div>      return result</div><div>    }</div><div><br></div><div><div>    // without `where`, `guard`:</div><div><div>    func mapValues&lt;T&gt;(excludingKeys keySet: Set&lt;Key&gt;, @noescape valueMap: (Value) -&gt; T) -&gt; [Key:T] {</div><div>      guard !keySet.isEmpty else { </div><div>        return self.mapValues(valueMap) </div><div>      }</div><div>      var result: [Key:T] = [:]</div><div>      for (key,value) in self {</div><div>        guard keySet.contains(key) else { </div><div>          continue </div><div>        }</div><div>        result[key] = valueMap(result)</div><div>      }</div><div>      return result</div><div>    }</div><div><br></div></div></div></div><div>  }</div><div><br></div><div>…where again I don’t like “continue” `guard` and thus would wind up picking the `if` variant, which adds another level of nesting (or use `.lazy.filter` and trust the compiler’s going to boil away the overhead for me).</div><div><br></div><div>So in conclusion, IMHO `where` on a `for`-`in` is a *modest* improvement in clarity when considered in isolation, but is handier than it may initially seem b/c it can allow for broader overall consistency of style.</div><div><br></div><div>I thus would be in favor of keeping `where` on for-in, or if it must be removed doing so with the intent to restore some better-designed equivalent shortly after removal.</div><br><blockquote type="cite"><div><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div dir="ltr"><div></div><div><div><br><div class="gmail_quote"><div dir="ltr">On Fri, Jun 10, 2016 at 10:17 AM Xiaodi Wu &lt;<a href="mailto:xiaodi.wu@gmail.com" target="_blank">xiaodi.wu@gmail.com</a>&gt; wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div style="white-space:pre-wrap">I think this idea--if you don&#39;t like it, then you don&#39;t have to use it--is indicative of a key worry here: it&#39;s inessential to the language and promotes dialects wherein certain people use it and others wherein they don&#39;t. This is an anti-goal.<br></div><br><div class="gmail_quote"><div dir="ltr">On Fri, Jun 10, 2016 at 12:10 let var go &lt;<a href="mailto:letvargo@gmail.com" target="_blank">letvargo@gmail.com</a>&gt; wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div dir="ltr">Leave it in!<div><br><div>It&#39;s a great little tool. I don&#39;t use it very often, but when I do it is because I&#39;ve decided that in the context of that piece of code it does exactly what I want it to do with the maximum amount of clarity.</div><div><br></div></div><div>If you don&#39;t like it, then don&#39;t use it, but I can&#39;t see how it detracts from the language at all.</div><div><br></div><div>The *only* argument that I have heard for removing it is that some people don&#39;t immediately intuit how to use it. I didn&#39;t have any trouble with it at all. It follows one of the most basic programming patterns ever: &quot;For all x in X, if predicate P is true, do something.&quot; The use of the keyword &quot;where&quot; makes perfect sense in that context, and when I read it out loud, it sounds natural: &quot;For all x in X where P, do something.&quot; That is an elegant, succinct, and clear way of stating exactly what I want my program to do.</div><div><br></div><div>I don&#39;t doubt that it has caused some confusion for some people, but I&#39;m not sold that that is a good enough reason to get rid of it. It seems strange to get rid of a tool because not everyone understands how to use it immediately, without ever having to ask a single question. As long as its not a dangerous tool (and it isn&#39;t), then keep it in the workshop for those times when it comes in handy. And even if there is some initial confusion, it doesn&#39;t sound like it lasted that long. It&#39;s more like, &quot;Does this work like X, or does this work like Y? Let&#39;s see...oh, it works like X. Ok.&quot; That&#39;s the entire learning curve...about 5 seconds of curiosity followed by the blissful feeling of resolution.</div></div><br><div class="gmail_quote"><div dir="ltr">On Fri, Jun 10, 2016 at 9:32 AM Xiaodi Wu via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>&gt; wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On Fri, Jun 10, 2016 at 11:23 AM, Sean Heber via swift-evolution <span dir="ltr">&lt;<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><span>&gt; And to follow-up to myself once again, I went to my &quot;Cool 3rd Party Swift Repos&quot; folder and did the same search. Among the 15 repos in that folder, a joint search returned about 650 hits on for-in (again with some false positives) and not a single for-in-while use.<br>
<br>
</span>Weird. My own Swift projects (not on Github :P) use “where” all the time with for loops. I really like it and think it reads *and* writes far better as well as makes for nicer one-liners. In one project, by rough count, I have about 20 that use “where” vs. 40 in that same project not using “where”.<br>
<br>
In another smaller test project, there are only 10 for loops, but even so one still managed to use where.<br>
<br>
Not a lot of data without looking at even more projects, I admit, but this seems to suggest that the usage of “where” is going to be very developer-dependent. Perhaps there’s some factor of prior background at work here? (I’ve done a lot of SQL in another life, for example.)<br></blockquote><div><br></div></div></div></div><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div>That is worrying if true, because it suggests that it&#39;s enabling &#39;dialects&#39; of Swift, an explicit anti-goal of the language.</div></div></div></div><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
<br>
I feel like “where” is a more declarative construct and that we should be encouraging that way of thinking in general. When using it, it feels like “magic” for some reason - even though there’s nothing special about it. It feels like I’ve made the language work *for me* a little bit rather than me having to contort my solution to the will of the language. This may be highly subjective.<br>
<br>
l8r<br>
<span><font color="#888888">Sean<br>
</font></span><div><div><br>
_______________________________________________<br>
swift-evolution mailing list<br>
<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a><br>
<a href="https://lists.swift.org/mailman/listinfo/swift-evolution" rel="noreferrer" target="_blank">https://lists.swift.org/mailman/listinfo/swift-evolution</a><br>
</div></div></blockquote></div></div></div>
_______________________________________________<br>
swift-evolution mailing list<br>
<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a><br>
<a href="https://lists.swift.org/mailman/listinfo/swift-evolution" rel="noreferrer" target="_blank">https://lists.swift.org/mailman/listinfo/swift-evolution</a><br>
</blockquote></div>
</blockquote></div>
</blockquote></div></div></div></div>
</blockquote></div><br></div></div>
_______________________________________________<br>swift-evolution mailing list<br><a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a><br><a href="https://lists.swift.org/mailman/listinfo/swift-evolution" target="_blank">https://lists.swift.org/mailman/listinfo/swift-evolution</a><br></div></blockquote></div><br></div><br>_______________________________________________<br>
swift-evolution mailing list<br>
<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a><br>
<a href="https://lists.swift.org/mailman/listinfo/swift-evolution" rel="noreferrer" target="_blank">https://lists.swift.org/mailman/listinfo/swift-evolution</a><br>
<br></blockquote></div><br></div></div>