[swift-evolution] [Review] SE-0091: Improving operator requirements in protocols

Tony Allevato allevato at google.com
Wed May 18 16:38:25 CDT 2016


On Wed, May 18, 2016 at 2:20 PM Matthew Johnson <matthew at anandabits.com>
wrote:

> On May 18, 2016, at 4:06 PM, Tony Allevato <allevato at google.com> wrote:
>
> On Wed, May 18, 2016 at 1:56 PM Matthew Johnson via swift-evolution <
> swift-evolution at swift.org> wrote:
>
>>
>> Personally, I find Rust’s approach a bit ugly.  And Tony makes a very
>> good point in the proposal that using words requires us to learn the word
>> associated with each operator.
>>
>
> Right—and in addition to the cognitive overload of knowing (and
> remembering) the word associated with each operator, it introduced bloat in
> those interfaces. End users of such an interface may question why there is
> a named method for that operator, and whether the named method and the
> operator function differently.
>
> Likewise, in many cases (such as arithmetic operations), it seems
> contrived to come up with names for an operator where that operator is
> already a term of art that can express the idea better than the words can.
>
>
> I noted some concerns about this proposal not including automatic
>> trampolines.  However, if we are not going to be able to make a breaking
>> change like this in the standard library after Swift 3 I do think it is
>> important to give this proposal significant consideration even without
>> them.  Automatic trampolines can be added later but we may not have the
>> opportunity to fix the standard library protocols later.
>>
>
> I wish I had been able to keep automatic trampolines in—I thought it was
> the "killer feature" that brings the whole proposal together. (Hey core
> team, you can still change your mind! :)
>
> That being said, I feel that the named-method approach is a huge step in
> the wrong direction and this proposal is strong enough without them to
> improve other existing ones, such as FloatingPoint, and would go a long way
> toward cleaning up the language in other extremely common cases (like
> anything that conforms to Equatable).
>
>
> Tony (and core team), do you have any thoughts on the specific concerns I
> brought up?
>

> Imagine this:
>
> protocol P {
>     static func ++++(lhs: Self, rhs: Self) -> Self
> }
> func ++++ <T: P>(lhs: T, rhs: T) -> T {
>     return T.++++(lhs, rhs)
> }
>
> protocol Q {
>
>     static func ++++(lhs: Self, rhs: Self) -> Self
> }
> func ++++ <T: Q>(lhs: T, rhs: T) -> T {
>     return T.++++(lhs, rhs)
> }
>
> struct S: P, Q {
>     static func ++++(lhs: Self, rhs: Self) -> Self {
>         // ...
>     }
> }
>
> let s1 = S()
> let s2 = S()
> let s3 = s1 ++++ s2 // compiler error, both trampolines are an equally
> good match, resulting in ambiguity
>
> // we have to add the following to resolve the ambiguity:
>
> func ++++(lhs: S, rhs: S) -> S {
>     return S.++++(lhs, rhs)
> }
>
> You could argue that this is a contrived example and is unlikely to happen
> in real code.  My point is partly that it’s a regression from current state
> and partly that it is very likely to be confusing if people run into it.
>

Agreed. Your suggestion of explicitly clarifying it on the concrete type is
the most obvious one I can think of, as well. I'd be interested in the core
team's thoughts as well—I'm inclined to say that since it's likely to be
rare, and since the more specific global operator lets us escape through a
trap door, it's acceptable, even though I have to acknowledge that it's not
ideal and I'm not 100% happy with that.



>
>
>
>
>
>>
>>
>>
>>>
>>>
>>>>
>>>>
>>>>
>>>>>
>>>>>
>>>>>>
>>>>>>
>>>>>>> 3) As has already been noted by many others, the suggested syntax for
>>>>>>> prefix/postfix operators is overcomplicated. The proposal is:
>>>>>>>
>>>>>>> // These are deprecated, of course, but used here just to serve as an
>>>>>>> // example.
>>>>>>> static prefix func ++(_ value: inout Self) -> Self
>>>>>>> static postfix func ++(_ value: inout Self) -> Self
>>>>>>>
>>>>>>> We don't need that. Since the 'operators' declared inside protocols
>>>>>>> are
>>>>>>> effectively just normal methods (apart from their names), we just
>>>>>>> need to
>>>>>>> name the parameters accordingly:
>>>>>>>
>>>>>>> static func ++(prefix value: inout Self) -> Self
>>>>>>> static func ++(postfix value: inout Self) -> Self
>>>>>>>
>>>>>>> 4) I don't agree with the request to limit to static methods for the
>>>>>>> operator implementations.
>>>>>>> I support this for symmetrical binary operators like +, but there
>>>>>>> are other
>>>>>>> operators like += that seem to work better with members. That is, the
>>>>>>> proposed declaration:
>>>>>>>
>>>>>>> static func +=(_ lhs: inout Self, _ rhs: Self)
>>>>>>>
>>>>>>> is more similar to the global += operator definition, but is less
>>>>>>> clear than:
>>>>>>>
>>>>>>> mutating func +=(_ rhs: Self)
>>>>>>>
>>>>>>> this is apparent also at the call site. With the proposed syntax,
>>>>>>> one would
>>>>>>> need to do:
>>>>>>>
>>>>>>> func +=<T: Foo>(_ lhs: inout T, _ rhs: T) {
>>>>>>>     T.+=(lhs, rhs)
>>>>>>> }
>>>>>>>
>>>>>>> while with a member function this would read more naturally as:
>>>>>>>
>>>>>>> func +=<T: Foo>(_ lhs: inout T, _ rhs: T) {
>>>>>>>     lhs.+=(rhs)
>>>>>>> }
>>>>>>>
>>>>>>
>>>>>> I considered this, but eventually settled on "everything is static"
>>>>>> for consistency. As you mention, there's a stronger argument to be made for
>>>>>> assignment operators to have "left hand side is the receiver" semantics
>>>>>> than there are for standard infix operators, but from a consistency point
>>>>>> of view (and ease of learning), I think having everything static and the
>>>>>> signatures of the static operators matching those of the global operators
>>>>>> is preferable.
>>>>>>
>>>>>
>>>>> I think this would better be left as a choice to the author of the
>>>>> protocol. There doesn't seem to be any technical reason to place this
>>>>> restriction.
>>>>>
>>>>
>>>>>
>>>>>> (Which is also why, as I mentioned in a previous reply, I would be
>>>>>> open to dropping the prefix/postfix keyword and making it an argument label
>>>>>> instead, in both contexts.)
>>>>>>
>>>>>>
>>>>>>>
>>>>>>> 5) the proposal mentions the open question of ambiguities between
>>>>>>> the dot
>>>>>>> syntax to access methods and operators whose name starts with a dot.
>>>>>>> This seems to be a real issue: I don't think
>>>>>>>
>>>>>>> return T....(minimum, maximum)
>>>>>>>
>>>>>>> looks any good, even if the compiler was able to parse it.
>>>>>>>
>>>>>>> However, this just means that the methods used to implement
>>>>>>> operators with
>>>>>>> problematic names would need to use different names. Arguably, the
>>>>>>> only
>>>>>>> cases where one would really want to use methods with operator names
>>>>>>> is for
>>>>>>> arithmetical operators. Custom operators like ... are better
>>>>>>> expressed as
>>>>>>> methods with more significant names.
>>>>>>>
>>>>>>
>>>>>> If there is a strong case where an operator is better implemented as
>>>>>> a global operator and a named method, this proposal still allows that,
>>>>>> since it's not deprecating all global operator definitions. A protocol
>>>>>> could certainly have a requirement that is a named method, and provide a
>>>>>> global generic operator that calls it.
>>>>>>
>>>>>>
>>>>>>>
>>>>>>> 6) It seems somewhat arbitrary to restrict method names to match an
>>>>>>> operator, nor to put requirements on the function signature. I'd say
>>>>>>> there
>>>>>>> are two cases, either the compiler can handle a method name that uses
>>>>>>> special characters, or it can't. If it can't, matching an operator
>>>>>>> name
>>>>>>> won't help. If it can, why put limits? There could be other creative
>>>>>>> uses of
>>>>>>> such names, which we would be ruling out for no particular reason.
>>>>>>> This is
>>>>>>> something that seems better left to the author of the protocol.
>>>>>>>
>>>>>>
>>>>>> IMO, to reduce potential confusion, I would argue that a function
>>>>>> whose name is the same as a defined operator should conform to the
>>>>>> requirements (such as argument count) of that operator. It's certainly
>>>>>> worth discussion, though! With that being said, it may be easier on users
>>>>>> to "rule something out" now and open it up later if need be, rather than to
>>>>>> leave it open for people to use and decide it needs to be closed later.
>>>>>>
>>>>>
>>>>> This doesn't seem different to me from having multiple functions with
>>>>> the same name and different signature, which Swift allows without problems.
>>>>> Again, I think this is a choice that the author of the protocol should
>>>>> make, and there doesn't seem to be any technical reason to require
>>>>> otherwise.
>>>>>
>>>>>
>>>>>>
>>>>>>
>>>>>>> 7) Automatic generation of trampoline functions is out of scope so
>>>>>>> I'm not
>>>>>>> going to talk much about it, I only want to mention that it would
>>>>>>> make sense
>>>>>>> to consider making such a feature as general as possible, instead of
>>>>>>> focusing exclusively on operators.
>>>>>>>
>>>>>>> For example, think of the common mathematical functions like sin,
>>>>>>> cos, etc.
>>>>>>> It could make sense to give them the same treatment as operators,
>>>>>>> declaring
>>>>>>> them as part of the FloatingPoint protocol but preserving the global
>>>>>>> functions too.
>>>>>>> It might even make sense to be able to create trampolines not only
>>>>>>> from
>>>>>>> global space to a type, but also from one type to another type, or
>>>>>>> even for
>>>>>>> all methods of a type (e.g. when boxing a value inside another type).
>>>>>>>
>>>>>>> > * Is the problem being addressed significant enough to warrant a
>>>>>>> change to
>>>>>>> Swift?
>>>>>>>
>>>>>>> Absolutely. The handling of operators in protocols has been one of
>>>>>>> the worst
>>>>>>> pain points in my use of Swift.
>>>>>>>
>>>>>>> > * Does this proposal fit well with the feel and direction of Swift?
>>>>>>>
>>>>>>> Yes; it significantly increases clarity and consistency.
>>>>>>>
>>>>>>> > * If you have used other languages or libraries with a similar
>>>>>>> feature,
>>>>>>> how do you feel that this proposal compares to those?
>>>>>>>
>>>>>>> I only have experience with C++ operator overloading, which is much
>>>>>>> less
>>>>>>> advanced.
>>>>>>>
>>>>>>> > * How much effort did you put into your review? A glance, a quick
>>>>>>> reading,
>>>>>>> or an in-depth study?
>>>>>>>
>>>>>>> An in-depth study of the proposal, and I read all the relevant
>>>>>>> threads on
>>>>>>> the mailing list.
>>>>>>>
>>>>>>> --
>>>>>>> Nicola
>>>>>>>
>>>>>>>
>>>>>>> _______________________________________________
>>>>>>> swift-evolution mailing list
>>>>>>> swift-evolution at swift.org
>>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>>>>>
>>>>>>
>>>
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org
>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>
>>>
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>
>>
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160518/387b8ebf/attachment.html>


More information about the swift-evolution mailing list