<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=""><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="">Hi, Tony. Thanks for working on this. I have to say I’m incredibly concerned with this direction, for two main reasons. I’ll try to break them down here. (Sorry for squeaking in at the end of the review period!)</div><div class=""><br class=""></div><div class=""><b class="">Overrides</b></div><div class=""><b class=""><br class=""></b></div><div class="">People up until now have been fairly unhappy with how operators are statically dispatched and can’t be overridden. We went all the way towards providing == that automatically calls isEqual(_:) on NSObject, but then you provide == for your subclass and it never gets called…no, wait, it <i class="">does</i> get called when you do a simple test, but not from the actual code that has NSObject as a static type.</div><div class=""><br class=""></div><div class="">This proposal stays in that space: the proposed “trampoline” operator will dispatch based on the <i class="">static</i> type of the objects, not the dynamic type. Why? Consider using == on an NSURL and an NSString, both statically typed as NSObject. Given the definition of the trampoline from the proposal</div><div class=""><br class=""></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class="">func == <T: Equatable>(lhs: T, rhs: T) -> Bool {</div><div class=""> return T.==(lhs, rhs)</div><div class=""><div class="">}</div></div></blockquote><div class=""><br class="">T can’t possibly be anything but NSObject. (Neither NSURL nor NSString matches the types of both ‘lhs’ and ‘rhs’.) This isn’t a regression from the current model, as you say, but it does make the current model <i class="">even more surprising,</i> since normally you’d expect methods to be dynamically dispatched.</div><div class=""><br class=""></div><div class="">Here’s an alternate formation of the trampoline that’s a little better about this…</div><div class=""><b class=""><br class=""></b></div><div class=""><blockquote style="margin: 0px 0px 0px 40px; border: none; padding: 0px;" class=""><div class="">func == <T: Equatable>(lhs: T, rhs: T) -> Bool {</div><div class=""> return lhs.dynamicType.==(lhs, rhs)</div><div class="">}</div></blockquote></div><div class=""><br class=""></div><div class="">…but I’m still not convinced. (People are especially likely to get this wrong without the trampolines being synthesized.)</div><div class=""><br class=""></div><div class="">One more note: at one point Joe Groff was investigating the idea that conformances wouldn’t be inherited onto subclasses, which would mean no more implicit ‘required’ initializers. Instead, the compiler would perform most operations by upcasting to the base class, and then converting to the protocol type or calling the generic function. In this world, T would <i class="">always</i> be NSObject, never a subclass, and we’d have to come up with something else. I think this model is still worth investigating and I wouldn’t want to close off our options just for the sake of “cleaning house”.</div><div class=""><br class=""></div><div class="">It’s possible that there’s not actually a reason to override operators in practice, which would make pretty much all of these concerns go away. (== is special; imagine we had an operation for checking equality within types and one across type hierarchies and ignore it for now.) I think it’d be worth investigating where operators are overridden today, and not just in Swift, to make sure we cover those use cases too.</div><div class=""><br class=""></div><div class="">(Please forgive all of the Foundation type examples that may soon be value types. They’re convenient.)</div><div class=""><br class=""></div><div class=""><br class=""></div><div class=""><b class="">Assignment Operators</b></div><div class=""><b class=""><br class=""></b></div><div class="">A mutating requirement and a static method with an inout parameter mean different things for a conforming class: the former can only access the class’s properties, while the latter can replace the caller’s <i class="">reference</i> as well.</div><div class=""><br class=""></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class="">class Player { … }</div><div class=""><br class=""></div><div class="">extension Player {</div><div class=""> static func roulette(_ player: inout Player) {</div><div class=""> if randomValue() > 0.1 {</div><div class=""> player.roundsSurvived += 1</div><div class=""> } else {</div><div class=""> // Replace this player…but not any other references to them!</div><div class=""> player = Player()</div><div class=""> }</div><div class=""> }</div><div class=""><br class=""></div><div class=""> /*mutating*/ func solitaire() {</div><div class=""> self.roundsSurvived += 1</div><div class=""> // Cannot replace ‘self’</div><div class=""> //self = Player()</div><div class=""> }</div><div class="">}</div></blockquote><div class=""><br class=""></div><div class="">I’m not sure if one of these is obviously better than the other (more capable ↔︎ more footgun). I agree with Nicola's point about mutating methods <i class="">looking</i> better than static methods taking an inout parameter, but that probably shouldn’t be the ultimate deciding factor.</div><div class=""><br class=""></div><div class=""><br class=""></div><div class="">I know we want to improve type-checker performance, and reducing the number of overloads <i class="">seems</i> like a way to do that, but I’m not convinced it actually will in a significant way (e.g. “you can now use ten operators in a chain instead of seven” is not enough of a win). It still seems like there ought to be a lot of low-hanging fruit in that area that we could easily clear away, like “an overload containing a struct type will never match any input but that struct type”.</div><div class=""><br class=""></div><div class="">I personally really want to move operators into types, but I want to do it by doing member lookup on the type, and fall back to global operators only if something can’t be found there. That approach</div><div class=""><br class=""></div><div class="">- also has potential to improve type-checker performance</div><div class="">- also removes operators from the global namespace</div><div class="">- also removes the need for “override points” (implementation methods like NSObject.isEqual(_:) and FloatingPoint.isLessThan(_:))</div><div class=""><br class=""></div><div class="">It does privilege the left-hand side of a binary operator, but I think that’s acceptable for the operators we’ve seen in practice. (Of course we would need real data to back that up.)</div><div class=""><br class=""></div><div class=""><br class=""></div><div class="">I think that about sums up my concerns and my interest in an alternate proposal. Again, I’m sorry for coming to this so late and for skimming the latest discussion on it; I’m sure “my” proposal has already come up, and I know it has its own flaws. I think I’m just not convinced that this is sufficiently <i class="">better</i> to be worth the churn and closing off of other potential avenues.</div><div class=""><br class=""></div><div class="">Best,</div><div class="">Jordan</div></body></html>