<div dir="ltr">I&#39;m +1 on Nicola&#39;s suggestions.<div><br></div><div>Basically, remove operators from protocols, simplifying the language. This doesn&#39;t prevent operators from using protocols (through generics).</div><div><br></div><div>Operators with protocols can still be defined as Nicola suggested, through a regular protocol method and a generic. Symmetrical operators can use a static method on the protocol if that&#39;s desirable. Swift has all the functionality for this already.</div><div><br></div><div>It also doesn&#39;t have the downside of preventing operators from working on things like types, tuples, etc.</div><div><br></div></div><div class="gmail_extra"><br><div class="gmail_quote">On Mon, Feb 29, 2016 at 5:24 PM, Thorsten Seitz 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:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class=""><br>
&gt; Am 26.02.2016 um 17:55 schrieb Sean Heber via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a>&gt;:<br>
&gt;<br>
&gt; I agree with all of this. I’m not sure operators really belong *inside* of the type. IMO, attempting to include them within the type is only desirable because of the way they are declared inside of the protocol. I think the asymmetry should be addressed on that side instead (if at all).<br>
<br>
</span>Good point! Maybe we should think about turning operators into multi-methods which are dynamically dispatched on all arguments like Dylan had. Or at least just move their declaration out of protocols.<br>
<br>
-Thorsten<br>
<div class="HOEnZb"><div class="h5"><br>
&gt;<br>
&gt; l8r<br>
&gt; Sean<br>
&gt;<br>
&gt;<br>
&gt;<br>
&gt;&gt; On Feb 26, 2016, at 10:43 AM, plx via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a>&gt; wrote:<br>
&gt;&gt;<br>
&gt;&gt; After careful consideration, I am not sure this sort of thing is actually a change that would *actually* be an obvious improvement.<br>
&gt;&gt;<br>
&gt;&gt; # First Concern: Code-Organization/Ownership Issues<br>
&gt;&gt;<br>
&gt;&gt; Although it is easy to see how to handle operators that have *homogenous* types — e.g. anything like `func +(lhs: T, rhs: T) -&gt; T` — it’s really unclear how a proposal like this *should* work for operators that have *heterogeneous* types — e.g. anything like `func *(lhs: T, rhs: U) -&gt; U ` (etc.).<br>
&gt;&gt;<br>
&gt;&gt; Since we’re talking about operators, this isn’t really a hypothetical concern, either:<br>
&gt;&gt;<br>
&gt;&gt; ## Example: Vector/Matrix Operations<br>
&gt;&gt;<br>
&gt;&gt; func *(lhs: Matrix4x4, rhs: Vector4) -&gt; Vector4<br>
&gt;&gt; func *(lhs: Vector4, rhs: Matrix4x4) -&gt; Vector4<br>
&gt;&gt;<br>
&gt;&gt; Both operations are reasonable to define, but defining the operator as instance methods seems to leave you in a rather awkward spot:<br>
&gt;&gt;<br>
&gt;&gt; - perhaps one is implemented by `Matrix4x4` and the other by `Vector4` (really odd code organization imho…)<br>
&gt;&gt; - perhaps both are implemented by, say, `Matrix4x4`, but one of them is using nonstandard syntax (defeating the point of custom operators, imho)<br>
&gt;&gt; - perhaps the proposal lets an operator-function declaration indicate *which* argument is `self` (new syntax/new semantics)<br>
&gt;&gt;<br>
&gt;&gt; …whereas the “operators are static functions” approach makes it reasonable to have both versions defined at the same scope (and e.g. &quot;near each other”).<br>
&gt;&gt;<br>
&gt;&gt; I know the specific proposal here wouldn’t eliminate the ability to define the operators as currently, but it’d be a shame to be unable to include things like the above method in protocols.<br>
&gt;&gt;<br>
&gt;&gt; ## Example: Scalar/Vector Operations<br>
&gt;&gt;<br>
&gt;&gt; Similarly, at a more-basic level, you might *want* this:<br>
&gt;&gt;<br>
&gt;&gt; protocol VectorType : Equatable {<br>
&gt;&gt;  typealias Component : NumericType // if it existed<br>
&gt;&gt;<br>
&gt;&gt;  // to match convention, scalars go *in front* of vectors<br>
&gt;&gt;  operator *(lhs: Int, rhs: Self) -&gt; Self<br>
&gt;&gt;  operator *(lhs: Self.Component, rhs: Self) -&gt; Self<br>
&gt;&gt;<br>
&gt;&gt;  // but why should we not be flexible on this point?<br>
&gt;&gt;  operator *(lhs: Self, rhs: Int) -&gt; Self<br>
&gt;&gt;  operator *(lhs: Self, rhs: Self.Component) -&gt; Self<br>
&gt;&gt; }<br>
&gt;&gt;<br>
&gt;&gt; …and are we going to make `struct Vector4`’s conformance to `VectorType` contingent on the presence of extension methods on `Int` (etc.)?<br>
&gt;&gt;<br>
&gt;&gt; That just seems really unintuitive and counterproductive.<br>
&gt;&gt;<br>
&gt;&gt; # Second Concern: Dynamic Operator Dispatch Not Really Necessary<br>
&gt;&gt;<br>
&gt;&gt; What I mean is, in the use cases that come to mind for dynamic operator-dispatch. making operators dynamically-dispatched wouldnt’ actually provide any benefit over what you can achieve today with protocols.<br>
&gt;&gt;<br>
&gt;&gt; EG, for `==`, consider a protocol and operators like below:<br>
&gt;&gt;<br>
&gt;&gt; protocol DynamicEquatable : class {<br>
&gt;&gt;  func isEqual(other: Self) -&gt; Bool<br>
&gt;&gt; }<br>
&gt;&gt;<br>
&gt;&gt; func ==&lt;T:DynamicEquatable&gt;(lhs: T, rhs: T) -&gt; Bool {<br>
&gt;&gt;  return (lhs === rhs) || lhs.isEqual(rhs)<br>
&gt;&gt; }<br>
&gt;&gt;<br>
&gt;&gt; func !=&lt;T:DynamicEquatable&gt;(lhs: T, rhs: T) -&gt; Bool {<br>
&gt;&gt;  return (lhs !== rhs) &amp;&amp; !lhs.isEqual(rhs)<br>
&gt;&gt; }<br>
&gt;&gt;<br>
&gt;&gt; …which as far as I can tell gets you back to the same place you’d be if you had a dynamically-dispatched `==` (both in terms of *what it would do* and also *what issues it would still have*).<br>
&gt;&gt;<br>
&gt;&gt; Is there some (beneficial?) aspect of making `==` dynamically-dispatched that isn’t also present in the above design?<br>
&gt;&gt;<br>
&gt;&gt; Are there operators for which there would be a material difference between the operator being dynamically-dispatched and the operator being defined over a protocol that has a dynamically-dispatched method providing the implementation?<br>
&gt;&gt;<br>
&gt;&gt; # Remark<br>
&gt;&gt;<br>
&gt;&gt; For `==` in particular, you could *maybe* improve the above slightly if Swift had a way to write a where clause like `U:&gt;T` — meaning “U is a subclass of T, but not T itself” — as then you could add variants like:<br>
&gt;&gt;<br>
&gt;&gt; func ==&lt;T:DynamicEquatable,U:DynamicEquatable where T:&gt;U&gt;(lhs: T, rhs: U) -&gt; Bool {<br>
&gt;&gt;  return (lhs === rhs) || lhs.isEqual(rhs)<br>
&gt;&gt; }<br>
&gt;&gt;<br>
&gt;&gt; func ==&lt;T:DynamicEquatable,U:DynamicEquatable where U:&gt;T&gt;(lhs: T, rhs: U) -&gt; Bool {<br>
&gt;&gt;  return (lhs === rhs) || rhs.isEqual(lhs)<br>
&gt;&gt; }<br>
&gt;&gt;<br>
&gt;&gt; …which would hopefully make a direct call into the more-derived type’s implementation of `isEqual`, which would be more-likely to contain a fast path, but even so it’s not obvious that there’s all that much of an actual win to be had here, in practice.<br>
</div></div><div class="HOEnZb"><div class="h5">_______________________________________________<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>
</div></div></blockquote></div><br></div>