The lesson I took away from SE-0110 is that conveniences, once offered, are disruptive to take away. People who don&#39;t delve deeply into the reasons that make the conveniences unworkable in the grand scheme of things are upset when these features are taken away because specific use cases are made less ergonomic.<br><br>Since, as you say, subtyping is always a bit perilous, if this idea is adopted but turns out to have important overlooked problems, trying to roll it back is going to cause a lot of pain. Not offering it in the first place, however, is by comparison only a slight inconvenience. No one has ever died of not drinking alcohol; plenty have died of alcohol withdrawal. Therefore, my lesson from SE-0110 is: vegetables now, vegetables forever.<br><div class="gmail_quote"><div dir="ltr">On Thu, Jul 6, 2017 at 21:38 Brent Royal-Gordon via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a>&gt; wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word"><div><blockquote type="cite"><div>On Jul 6, 2017, at 9:13 AM, Dave Abrahams &lt;<a href="mailto:dabrahams@apple.com" target="_blank">dabrahams@apple.com</a>&gt; wrote:</div><br class="m_-765812833032746280Apple-interchange-newline"><div><span style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;float:none;display:inline!important">I&#39;m not sure what you&#39;re objecting to about this.  Is it the very</span><br style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><span style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;float:none;display:inline!important">appearance of curly braces?</span></div></blockquote></div><div><br></div></div><div style="word-wrap:break-word"><div>I went to bed thinking that maybe I should have explained that better, and I guess I was right. ;^) Here&#39;s why I think we should do something here.</div><div><br></div><div>From what I can tell, mapping or filtering on a single property is exceptionally common. I ran a few regexes on the Swift code present on my machine (basically the stuff in the Swift repos, plus my open-source projects, plus my closed-source stuff, plus various playgrounds and things) to see how common different kinds of `map`, `filter`, and `flatMap` closures were:</div><div><br></div><span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span>2142<span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span>OP { …$0… }<br><span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span>1835<span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span>OP(function) or OP(some.method)<br><span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span>589<span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span><span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span>OP { $0.property } or OP { $0.some.property }<br><span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span>564<span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span><span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span>OP { $0.property }<br><span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span>525<span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span><span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span>OP { function(…$0…) } or OP { some.method(…$0…) }<br><span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span>186<span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span><span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span>OP { $0.method(…) }<br><span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span>153<span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span><span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span>OP { function($0) } or OP { some.method($0) }<br><span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span>100<span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span><span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span>OP { $0 as SomeType } or OP { $0 as? SomeType } or OP { $0 as! SomeType }<br><span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span>52<span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span><span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span>OP { $0.method() }<br><span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span>35<span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span><span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span>OP { collection[…$0…] } or OP { some.collection[…$0…] }<br><span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span>20<span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span><span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span>OP { collection[$0] } or OP { some.collection[$0] }<br><span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span>13<span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span><span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span>OP { $0! }<br><br>(Simple regex-based match of `map`, `flatMap`, and `filter` calls. Permits various spacing schemes and `try`. If you want to run it on a more representative sample, the script is here, requires fish(1): <a href="https://gist.github.com/brentdax/2a8ee2705c39e9948aafedbd81b1366f" target="_blank">https://gist.github.com/brentdax/2a8ee2705c39e9948aafedbd81b1366f</a>)<br><div> </div><div>So, at least in my unscientific sample, more than a quarter of map/filter/flatMap calls which use `$0` simply look up a property or chain of properties on it. If we want to make something about `map` and friends more convenient, this seems like a good place to look.</div><div><br></div><div>(Using a straight function is a few times more common than a property, but given that this is also the syntax used to abstract over a closure body, I&#39;m not sure how much weight to put on that fact.)</div><div><br></div><div>So what&#39;s wrong with what we have now? Syntactic weight. Consider this expression:</div><div><br></div><div><span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span>person.map { $<a href="http://0.name" target="_blank">0.name</a> }</div><div><br></div><div>The &quot;loudest&quot; parts of this expression are the closure brackets and the `$0`, but they are actually the *least* important. They do not express anything about what this line of code *does*; they exist solely to tell the compiler how to do it. They are pure glue code, and serve only to obscure the actual intent. Compare that to:</div><div><br></div><div><span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span>person.map(\.name)</div><div><br></div><div>Here, we still have a glue character (the `\`), but it&#39;s just one, and it&#39;s relatively inconspicuous compared to something like `$0`.</div><div><br></div><div>That&#39;s not *too* bad, though. It gets a lot worse when the key path is actually in a variable:</div><div><br></div><div><span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span>array.map { $0[keyPath: prop] }</div><div><br></div><div>Again, look at how much of this line is given over to adapting a line of code to the compiler—and how little that actually matters when understanding what the line does. The most important thing in that expression is `prop`, but it&#39;s completely lost in this sea of irrelevant syntax. Compare to:</div><div><br></div><div><span class="m_-765812833032746280Apple-tab-span" style="white-space:pre-wrap">        </span>array.map(prop)</div><div><br></div><div>Which puts that piece of information front and center.</div><div><br></div><div>If there was an argument that the case was too complex to handle nicely, I think we could justify leaving it alone. That&#39;s essentially what happened with the much-requested placeholder syntax: Lots of people wanted it, but critics pointed out the fundamental ambiguity of the syntax, and after spending gallons of electrons arguing about it, the proponents pretty much backed off. But key paths don&#39;t have that problem—they always work the same way and are completely unambiguous, so there&#39;s no scope for misinterpretation. And the cases key paths can handle appear to be about as common as the cases placeholder syntax was intended for.</div><div><br></div><div>Nor is there a strong argument that the suggested behavior is fundamentally &quot;weird&quot;. It&#39;s pretty natural to think of a `KeyPath&lt;Root, Value&gt;` as being a `(Root) -&gt; Value` function, and the `foo.map(\.bar)` syntax reads pretty straightforwardly as long as you know what `map` does.</div><div><br></div><div>There&#39;s one more reason I think we should do this. It is not about the technology; it is not even really about the ergonomics. It&#39;s more about language &quot;marketing&quot;, for lack of a better term.</div><div><br></div><div>I think we were all surprised by the SE-0110 backlash. But in hindsight, I think it&#39;s pretty easy to explain. During the Swift 3 and 4 cycles, we systematically stripped conveniences and sugar from higher-order functions. We have good reasons to do this; we want to pare things down, fix the foundations, and then build up new conveniences. Eat your vegetables now and you can have dessert later.</div><div><br></div><div>But we&#39;re entering our second year of eating nothing but vegetables. It&#39;s very difficult to maintain a strict diet forever, especially when—like the vast majority of Swift&#39;s users who don&#39;t participate in evolution or implementation—you don&#39;t really see the point of it. It&#39;s hard to blame them for being tired of it, or for complaining when yet another tasty food is pulled off the menu.</div><div><br></div><div>Offering a treat like this on occasion will help ease the pain of losing the things we *need* to take away. And this is a particularly good candidate because, although it&#39;s a convenience for higher-order functions—which is where the pain is felt—it has little to do with parameter handling, the area where we actually need to remove things and refactor. It&#39;s like a dessert of ultra-dark chocolate—it&#39;s a treat that doesn&#39;t set the actual goal back very far.</div><div><br></div><div>In the abstract, &quot;fundamentals now, sugar later&quot; is the right approach. But it can&#39;t be considered &quot;right&quot; if the users won&#39;t accept it. So let&#39;s look for opportunities to add conveniences where we can. Maybe this isn&#39;t the right feature—subtyping is always a bit perilous—but we should be on the lookout for features like this one, places where we can improve things for our functional programming fans without obstructing our own efforts to clean up parameter handling.</div></div><div style="word-wrap:break-word"><br><div>
<span class="m_-765812833032746280Apple-style-span" style="border-collapse:separate;font-variant-ligatures:normal;font-variant-east-asian:normal;line-height:normal;border-spacing:0px"><div><div style="font-size:12px">-- </div><div style="font-size:12px">Brent Royal-Gordon</div><div style="font-size:12px">Architechies</div></div></span>

</div>
<br></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>