<div><br><div class="gmail_quote"><div>On Wed, Jun 7, 2017 at 14:33 Gwendal Roué via swift-evolution <<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a>> 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>Le 7 juin 2017 à 17:15, Vladimir.S <<a href="mailto:svabox@gmail.com" target="_blank">svabox@gmail.com</a>> a écrit :</div><br class="m_-2576005043248980472Apple-interchange-newline"><div><div>On 07.06.2017 16:20, Gwendal Roué wrote:<br><blockquote type="cite"><blockquote type="cite">Le 7 juin 2017 à 15:11, Xiaodi Wu <<a href="mailto:xiaodi.wu@gmail.com" target="_blank">xiaodi.wu@gmail.com</a> <<a href="mailto:xiaodi.wu@gmail.com" target="_blank">mailto:xiaodi.wu@gmail.com</a>>> a écrit :<br><br>While SE-0025 was generally regarded as unfortunate, the thousands of emails that followed relitigating it were much, much worse.<br><br>The removal of implicit tuple splatting, which is *not* SE-0110, was approved on the understanding that it would be a regression until explicit tuple splatting is introduced. This tradeoff was considered and approved. It’s clear that you disagree, but that is not grounds to divert a necessary discussion on mitigating SE-0110 into relitigating something else.<br></blockquote>Push me out if you want, but will you push out those blatant wounds out as well?<br>Example 1<br>- return columns.index { (column, _) in column.lowercased() == lowercaseName }<br>+ return columns.index { $0.0.lowercased() == lowercaseName }<br></blockquote><br>Why not<br>columns.index { (arg: (column: String, _: Int)) in arg.column.lowercased() == lowercaseName }<br>?<br></div></div></blockquote><div><br></div></div></div><div style="word-wrap:break-word"><div>It would works, but it's less than ideal: the `args` name has no meaning. We have an extra type declaration instead of type inference. It's much longer. The clarity is tremendously reduced.</div></div><div style="word-wrap:break-word"><div><br></div><div>- return columns.index { (column, _) in column.lowercased() == lowercaseName }</div></div><div style="word-wrap:break-word"><div><div>+ return columns.index { (arg: (column: String, _)) in arg.column.lowercased() == lowercaseName }</div></div><div></div></div><div style="word-wrap:break-word"><div><br><blockquote type="cite"><div><div>Yes, I understand that first syntax short and not verbose, but the alternative you provided IMHO much worse than explicit type declaration in closure.<br></div></div></blockquote><div><br></div></div></div><div style="word-wrap:break-word"><div><div>It's not an "alternative": it's Swift 3.</div><div><br></div><div>Maybe you did already profit from it, but did not notice.</div></div></div><div style="word-wrap:break-word"><div><br><blockquote type="cite"><div><div><br><blockquote type="cite">Example 2 :<br>- .map { (mappedColumn, baseColumn) -> (Int, String) in<br>+ .map { (pair) -> (Int, String) in<br>+ let mappedColumn = pair.key<br>+ let baseColumn = pair.value<br></blockquote><br>Can't compile something like this even in Swift 3, could you provide a small code snippet for this?<br></div></div></blockquote><div><br></div></div></div><div style="word-wrap:break-word"><div>Sure:</div><div><br></div><div><div style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo"><span style="font-variant-ligatures:no-common-ligatures;color:#ba2da2"> let</span><span style="font-variant-ligatures:no-common-ligatures"> mapping: [</span><span style="font-variant-ligatures:no-common-ligatures;color:#703daa">String</span><span style="font-variant-ligatures:no-common-ligatures">: </span><span style="font-variant-ligatures:no-common-ligatures;color:#703daa">String</span><span style="font-variant-ligatures:no-common-ligatures">] = ...</span></div><div style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo"><span style="font-variant-ligatures:no-common-ligatures"> mapping</span><span style="font-variant-ligatures:no-common-ligatures">.map { (mappedColumn, baseColumn) -> (</span><span style="font-variant-ligatures:no-common-ligatures;color:rgb(112,61,170)">Int</span><span style="font-variant-ligatures:no-common-ligatures">, </span><span style="font-variant-ligatures:no-common-ligatures;color:rgb(112,61,170)">String</span><span style="font-variant-ligatures:no-common-ligatures">) </span><span style="font-variant-ligatures:no-common-ligatures;color:rgb(186,45,162)">in ... }</span></div></div></div><div style="word-wrap:break-word"><div><div style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo"><span style="font-variant-ligatures:no-common-ligatures;color:rgb(186,45,162)"><br></span></div><blockquote type="cite"><div><div><blockquote type="cite">Example 3 :<br>- .map { (table, columns) in "\(table)(\(columns.sorted().joined(separator: ", ")))" }<br>+ .map { "\($0.key)(\($0.value.sorted().joined(separator: ", ")))" }<br></blockquote><br>Same, why not<br><br>.map { (arg: (table: String, columns: [String])) in "\(arg.table)(\(arg.columns.sorted().joined(separator: ", ")))" }<br></div></div></blockquote><div><br></div></div></div><div style="word-wrap:break-word"><div><div>Same answer: the extra `args` works, but we still have an ergonomics regression from Swift 3.</div></div></div><div style="word-wrap:break-word"><div><br><blockquote type="cite"><div><div><blockquote type="cite">Example 4 :<br>- dictionary.first { (column, value) in column.lowercased() == orderedColumn.lowercased() }<br>+ dictionary.first { $0.key.lowercased() == orderedColumn.lowercased() }<br></blockquote><br>Same.<br></div></div></blockquote><div><br></div></div></div><div style="word-wrap:break-word"><div>Indeed.</div><div></div></div><div style="word-wrap:break-word"><div><br><blockquote type="cite"><div><div><br><blockquote type="cite">See also messages from Stephen Cellis, who shows how other kinds of developer code has lost expressivity and clarity with those changes that have been "considered and approved".<br></blockquote><br>Gwendal, no one saying that new syntax is better, that it is good thing that we lost the short syntax for tuple argumment deconstructions in closures.<br></div></div></blockquote><div><br></div></div></div><div style="word-wrap:break-word"><div>Good to hear :-)</div><div></div></div><div style="word-wrap:break-word"><div><br><blockquote type="cite"><div><div>But there is just no easy/obvious way to keep that syntax in Swift 4. The problem can't be solved just by not implementing SE-0110, as in Swift4 we should have two separate function types: one that takes single tuple argument and second that accepts a list of arguments, i.e. (Int,Int)->() and ((Int,Int))->() should be two different types now.<br></div></div></blockquote><div><br></div></div></div><div style="word-wrap:break-word"><div><div>Of course I understand that.</div><div><br></div><div>I expect the compiler to perform the expected conversions for ergonomics' sake.</div><div><br></div><div>I can understand that this sugar could be added *after* current problems which are not related to ergonomics are solved. I just want to make it clear that there is an ergonomics problem *now*, and that solving it should have a very high priority.</div></div></div></blockquote><div><br></div><div>If this was the point you wished to make, then there isn’t any disagreement, I don’t think. We *all* can obviously agree that there is a loss in ergonomics due to SE-0110.</div><div><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><div>This is not just SE-0110, this is also SE-0066, so, to be correct, you should propose to revisit it also.<br><br>Please look here:<br><br>func foo(_ x: Int, _ y: Int) {} // type(of: foo) should be (Int, Int)->()<br>func bar(_ x (Int, Int)) {} // type(of: bar) should be ((Int, Int))->()<br><br>The above is described in SE-0066. Then, you have a closure constants:<br><br>var fooClosure = {(x: Int, y: Int) in }<br>var barClosure = {(x: (Int, Int)) in }<br><br>what should be types of these closures? Obvious the same: (Int,Int)->() and ((Int,Int))->() respectively.<br></div></div></blockquote><div><br></div></div></div><div style="word-wrap:break-word"><div><div>I guess you mean, not the same: (Int, Int) -> () vs. ((Int, Int)) -> ().</div></div></div><div style="word-wrap:break-word"><div><br><blockquote type="cite"><div><div>Then you have a func that accepts ((Int,Int))->Int closure:<br><br>func schedule(callback: ((Int,Int))->()) {..}<br><br>, given type of foo func is (Int, Int)->() , do you suggest to allow sending foo to 'schedule' func? The same question is for fooClosure<br><br>schedule(callback: foo) // ??<br>schedule(callback: fooClosure) // ??<br></div></div></blockquote><div><br></div></div></div><div style="word-wrap:break-word"><div>Yes, I do. For ergonomics' sake.</div><div></div></div><div style="word-wrap:break-word"><div><br><blockquote type="cite"><div><div>Probably we can(if technically possible, I don't know) to always allow sending of function/closure with list of arguments when function with one tuple is required. I don't know how such exceptional rule would looks like inside type system of Swift, what should be result of 'foo is ((Int,Int))->()' then and 'type(of:foo) == type(of:bar)' in such case.<br>But this requires a formal proposal, review period and implementation(as I understand, better before Swift 4 release). Probably you can submit such proposal, go through the review period and help with implementation.<br></div></div></blockquote><div><br></div></div></div><div style="word-wrap:break-word"><div><div>Seriously I don't know if I have the skills to write such a proposal. It dives much too deep in the compiler internals for me to be efficient in the proposal process.</div><div><br></div><div>I thus hope that I did succeed showing how seriously bad are the regressions induced by SE-0110, and that a skilled language designer will sponsor an ergonomics-rescue proposal.</div><div><br></div><div>So far, the answer to the ergonomics regression reports have been much too often negative. I wish ergonomics had better support in the community. Very few regular developers talk here, unfortunately.</div></div></div></blockquote><div><br></div><div>As I mentioned in a reply above, re-reading SE-0029 gives a full account of the rationale for this series of proposals. It acknowledges that tuple splatting is useful and explicitly welcomes a complete design for future consideration. However, it states that this is *hard*, and that the reason for not having one is precisely what you said: no one with both the expertise and the time has stepped forward to do it. Again, I’m not sure we’re covering new ground here, or even disagreeing.</div><div><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><div></div></div></div></blockquote><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><div>In this case we'll have the same user-friendly closure/function parameters expirience but with respect to correct function types.<br></div></div></blockquote><div><br></div></div></div><div style="word-wrap:break-word"><div><div>That would be just great!</div></div></div><div style="word-wrap:break-word"><div><br><blockquote type="cite">But currently we have a situation: argument of type ((Int,Int))->() is required, and we provide argument of another type : (Int,Int)->() i.e. incorrect type.<br></blockquote><blockquote type="cite"><div><div>The only obvious solution here is using the common rule for type mismatch - disallow this.<br></div></div></blockquote><div><br></div></div></div><div style="word-wrap:break-word"><div><div>And the obvious and easy way leads to regressions.</div></div></div><div style="word-wrap:break-word"><div><br><blockquote type="cite"><div><div>Currently we have a number of suggestions how we can improve usability for the discussed problem:<br><br>* use 'let' syntax in closure argument list to deconstruct tuple argument<br><br>* use doubled parenthesis to deconstruct tuple argument: { ((key, value)) in .. }<br></div></div></blockquote><div><br></div></div></div><div style="word-wrap:break-word"><div><div>Both 'let' and doubled parenthesis fail in ergonomics.</div><div><br></div><div>Let me explain you why: when you provide a closure to a function, do you know if the function expects a single-tuple input, or a multiple-arguments input? You don't know.</div></div></div></blockquote><div><br></div><div>Wait, why don’t you know? This is a baffling statement for me. Of course you would know, by inspection, surely?</div><div><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><div></div><div>For example, take those three functions:</div><div><br></div><div><span class="m_-2576005043248980472Apple-tab-span" style="white-space:pre-wrap">        </span>func f(_ closure:(Int, Int) -> ())</div><div><div><span class="m_-2576005043248980472Apple-tab-span" style="white-space:pre-wrap">        </span>func g(_ closure:((Int, Int)) -> ())</div><div><div><span class="m_-2576005043248980472Apple-tab-span" style="white-space:pre-wrap">        </span>func h(_ closure:((a: Int, b: Int)) -> ())</div><div><br></div><div>If one can always write (as in Swift 3):</div><div><br></div><div><span class="m_-2576005043248980472Apple-tab-span" style="white-space:pre-wrap">        </span>f { (a, b) in ... }</div><div><div><span class="m_-2576005043248980472Apple-tab-span" style="white-space:pre-wrap">        </span>g { (a, b) in ... }</div></div><div><div><div><span class="m_-2576005043248980472Apple-tab-span" style="white-space:pre-wrap">        </span>c { (a, b) in ... }</div></div></div><div><br></div><div>Then one can easily deal with a badly fit closure signature.</div><div><br></div><div>This is most examplified by dictionaries. They always expose (key: Key, value: Value) tuples (their Element type). Problem is that 'key' and 'value' are identifiers that only matter for dictionaries, not for dictionary users. It's very important for dictionary users to forget about tuples, and the `key` and `value` words:</div><div><br></div><div><span class="m_-2576005043248980472Apple-tab-span" style="white-space:pre-wrap">        </span>// No pollution</div><div><span class="m_-2576005043248980472Apple-tab-span" style="white-space:pre-wrap">        </span>dictionary.map { (name, score) in ... }</div><div><br></div><div>That wish (let one easily deal with a badly fit closure signature) is also asked by Stephen Cellis in his functional explorations.</div></div></div></div></div><div style="word-wrap:break-word"><div><br><blockquote type="cite"><div><div>* generation of closure of correct type if closure is declared inside function call and arguments have no type annotations, i.e.<br> //schedule(callback: foo) // disallowed, type mismatch<br> //schedule(callback: fooClosure) // disallowed, type mismatch<br><br> // allowed. compiler will generate closure of type ((Int,Int))->() from this code<br> schedule { x,y in }<br><br> // type mismatch, this syntax defines closure of (Int,Int)->() type<br> //schedule { (x: Int, y: Int) in }<br><br>But because all of this are additional features that can be added later, and each required to be reviewed/discussed in details, core team can decide to delay such 'fix' for after-release period. Let's wait and see what core team had to say about this subject.<br></div></div></blockquote><div><br></div></div></div><div style="word-wrap:break-word"><div>I can't wait to hear from the core team.</div></div><div style="word-wrap:break-word"><div><br></div><div>Gwendal</div><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" rel="noreferrer" target="_blank">https://lists.swift.org/mailman/listinfo/swift-evolution</a><br>
</blockquote></div></div>