<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""><div><blockquote type="cite" class=""><div class="">On Jul 12, 2016, at 10:35 AM, Jordan Rose &lt;<a href="mailto:jordan_rose@apple.com" class="">jordan_rose@apple.com</a>&gt; wrote:</div><br class="Apple-interchange-newline"><div class=""><meta http-equiv="Content-Type" content="text/html charset=utf-8" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class="">[Proposal: <a href="https://github.com/apple/swift-evolution/blob/master/proposals/0091-improving-operators-in-protocols.md" class="">https://github.com/apple/swift-evolution/blob/master/proposals/0091-improving-operators-in-protocols.md</a> ]</div><div class=""><br class=""></div><div class="">I definitely think this is an improvement over the last version! Nice work, Tony and Doug.</div><div class=""><br class=""></div><div class="">I <i class="">am</i>&nbsp;a little confused about the implementation, though. The proposal says this:</div><div class=""><br class=""></div><div class=""><blockquote type="cite" class="">Instead, Swift should always perform operator lookup universally such that it sees all operators defined&nbsp;at either module scope or within a type/extension of a type. This gives us the syntactic improvements&nbsp;immediately and the natural Swift thing of defining your functionality within the type or an extension&nbsp;thereof just works.<br class=""></blockquote><br class="">and then later says</div><div class=""><br class=""></div><div class=""><blockquote type="cite" class="">Therefore, we can achieve the performance improvements by making that insight part of the semantic&nbsp;model: when we find all operators, we also find the operators in the protocols themselves. The&nbsp;operators in the protocols are naturally generic.<br class=""></blockquote><br class=""><blockquote type="cite" class="">Then, we say that we do not consider an operator function if it implements a protocol requirement,&nbsp;because the requirement is a generalization of all of the operator functions that satisfy that&nbsp;requirement. With this rule, we’re effectively getting the same effects as if users had declared&nbsp;trampoline operators, but it's automatic.<br class=""></blockquote><br class="">How do we know if an operator function implements a protocol requirement? </div></div></div></blockquote><div><br class=""></div><div>Well, we can track it explicitly in the modules that define the protocol and that define conformances of specific types to the protocol. Alternatively, we try the protocol requirements *first*. Once we’ve inferred the ‘Self’ type of the protocol (which is part of trying the protocol requirement), we can look up a conformance and the witness to see which witnesses should no longer be considered.</div><br class=""><blockquote type="cite" class=""><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class="">What happens when an operator function implements a protocol requirement, but is also more general than that?</div></div></div></blockquote><div><br class=""></div><div>*Right now*, it’s only really possible when you’re using a global generic operator, because we require exact type matches between requirements and witnesses. If/when we allow the witness to be a supertype of the requirement, you’ll start to see more of the semantic effects of this model, because shadowing the witness with the requirement can reject code that is well-formed now.</div><div><br class=""></div><div>That’s why the shadowing behavior needs to be part of the semantic model. Implementation will let us settle the exact details so we can state those semantics more precisely.</div><br class=""><blockquote type="cite" class=""><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class=""> And if we do find the implementation in the protocol, what conformance do we use to invoke the function when the types involved aren’t all 'Self’?</div></div></div></blockquote><div><br class=""></div><div>If there isn’t a reference to ‘Self’, type inference for the use of the protocol requirement will fail.</div><br class=""><blockquote type="cite" class=""><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class=""><br class=""></div><div class="">I still prefer the rule that says we perform lookup into the left type and the right type, then fall back to top-level scope for backwards compatibility.</div></div></div></blockquote><div><br class=""></div><div>We thought about it a lot, and it’s not implementable in a way that’s consistent with type inference, because one of the left or right types might not be known yet, and you also need to consider the context type for something like, e.g.,</div><div><br class=""></div><div><span class="Apple-tab-span" style="white-space:pre">        </span>let x: UInt = 1 + 2</div><div><br class=""></div><br class=""><blockquote type="cite" class=""><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class=""><br class=""></div><div class=""><br class=""></div><div class="">Separately from the lookup rules, I’m still unhappy with the class problem. The proposal states this:</div><div class=""><br class=""></div><div class=""><blockquote type="cite" class="">We expect classes to implement the static operators in the protocol using `class`&nbsp;methods instead of&nbsp;`static` methods, which allows subclases to override them.</blockquote><br class=""></div><div class="">However, if lookup only finds the method in the protocol, it’s unclear whether this will call a conforming class's method, a static type’s method, or a dynamic type’s method; if it’s not the last, it’s hardly an “override”. I maintain that this is the wrong behavior for any class hierarchy that <i class="">does</i>&nbsp;include heterogeneous operations, including "assignment operators, operators for chaining tasks, DSLs for constraint systems, etc” (me, from last time).</div><div class=""><br class=""></div><div class="">More from last time:</div><div class=""><br class=""></div><div class=""><blockquote type="cite" class=""><blockquote type="cite" class="">- for class types, regardless of whether one is a base of the other or both share a common third base type, neither static nor instance&nbsp;methods completely solve the problem and won't until/unless Swift supports multiple dispatch, and the proposed behavior is not a&nbsp;regression in those cases<br class=""><br class=""></blockquote>I guess I’m not convinced of the chain of reasoning here. “Multi-method dispatch is the most correct way to solve the problem” is fine;&nbsp;“therefore, anything short of that isn’t worth doing” is where I get stuck. Instance methods&nbsp;partially&nbsp;solve the problem, and it’s possible (again,&nbsp;no data on hand) that they solve the problem in the majority of cases.<br class=""><br class="">(It’s also possible that the prevalence of OO has made people prefer operators that can be dispatched based on the left-hand side, so I&nbsp;guess I’d want to go look at, e.g. Haskell and Perl to see what operators don’t fit in that bucket.)<br class=""><br class=""><br class="">I guess I’d summarize my stance as “this proposal enshrines our current problems with operator semantics in order to improve consistency in&nbsp;the syntax” (with “enshrines” meaning “makes harder to change later”), and that doesn’t seem like a good enough reason to change from&nbsp;what we have now.<br class=""></blockquote><br class=""></div><div class="">…and I have to say I still feel that way. It’s not clear how much of a performance win we’ll get, and it’s not clear these are the right semantics, and it <i class="">is</i>&nbsp;clear that operators interact poorly with classes.</div></div></div></blockquote><div><br class=""></div><div>This is a good point, and one that’d I’d missed from the previous discussions. Left-bias is probably defensible, but I’m happy to do the conservative thing here: require the operator to be ‘final’. That’s no worse than what we have today—you have to delegate to something that’s explicitly dynamically dispatched based on whichever of the types you choose—and leaves open the possibility of loosening the rule in the future.</div><br class=""><blockquote type="cite" class=""><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class="">Jordan</div><div class=""><br class=""></div><div class=""><div class="">P.S. The proposal also has this line:</div><div class=""><br class=""></div><div class=""><blockquote type="cite" class="">non-static operator method syntax be&nbsp;<b class="">deprecated</b>&nbsp;in Swift 2 and&nbsp;<b class="">removed</b>&nbsp;in Swift 3</blockquote><br class=""></div><div class="">which should be updated in one way or another.</div></div></div></div></blockquote><div><br class=""></div>Sure.</div><div><br class=""></div><div><span class="Apple-tab-span" style="white-space:pre">        </span>- Doug</div><div><br class=""></div></body></html>