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

Xiaodi Wu xiaodi.wu at gmail.com
Wed May 18 16:09:08 CDT 2016


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

>
> On May 18, 2016, at 3:48 PM, Xiaodi Wu via swift-evolution <
> swift-evolution at swift.org> wrote:
>
>
>
> On Wed, May 18, 2016 at 3:37 PM, Nicola Salmoria via swift-evolution <
> swift-evolution at swift.org> wrote:
>
>>
>>
>> On Wed, May 18, 2016 at 10:27 PM, Tony Allevato <allevato at google.com>
>> wrote:
>>
>>> On Wed, May 18, 2016 at 1:00 PM Nicola Salmoria <
>>> nicola.salmoria at gmail.com> wrote:
>>>
>>>> On Wed, May 18, 2016 at 8:03 PM, Tony Allevato <allevato at google.com>
>>>> wrote:
>>>>
>>>>> On Wed, May 18, 2016 at 10:02 AM Nicola Salmoria via swift-evolution <
>>>>> swift-evolution at swift.org> wrote:
>>>>>
>>>>>> > * What is your evaluation of the proposal?
>>>>>>
>>>>>> I'm generally in strong support, having long been a proponent of
>>>>>> removing
>>>>>> operators from protocols (the first occurrence was in this thread:
>>>>>> http://article.gmane.org/gmane.comp.lang.swift.evolution/7935)
>>>>>>
>>>>>> I have several comments about the details of the proposal, however.
>>>>>>
>>>>>> 1) At the beginning, in the "Proposed solution" section, the proposal
>>>>>> says
>>>>>> "This document does not propose that the current way of defining
>>>>>> operators
>>>>>> be removed or changed at this time. Rather, we describe an addition
>>>>>> that
>>>>>> specifically provides improvements for protocol operator
>>>>>> requirements."
>>>>>>
>>>>>> Later, however, there is a "Deprecation of non-static protocol
>>>>>> operators"
>>>>>> section which suggest to do exactly that, and this is reiterated in
>>>>>> the
>>>>>> "Impact on existing code" section.
>>>>>>
>>>>>> Since I think that the deprecation of global operator overloads is the
>>>>>> crucial point of the proposal, I assume that the former is an
>>>>>> oversight.
>>>>>>
>>>>>
>>>>> I could probably do a better job of clarifying the wording here. The
>>>>> proposal does *not* deprecate *all* global operator overloads. Global
>>>>> operators can still be implemented as they have been in Swift. So if you
>>>>> have a concrete type like `struct Matrix`, you can still define at the
>>>>> global level `func +(lhs: Matrix, rhs: Matrix) -> Matrix`.
>>>>>
>>>>> What's being deprecated is the current syntax used to define operator
>>>>> requirements inside protocols (by making the functions static) and the
>>>>> manner by which subtypes conform (ditto, through static methods instead of
>>>>> global functions).
>>>>>
>>>>
>>>> OK, I guess the unclear part is when you talk about "an addition that
>>>> specifically provides improvements for protocol operator requirements."
>>>> This is not just an addition; it's intended to completely replace the
>>>> protocol operator syntax.
>>>>
>>>>>
>>>>>
>>>>>> 2) The method signatures in the examples are not up to date with the
>>>>>> current
>>>>>> Swift 3 syntax. For example:
>>>>>>
>>>>>> protocol Equatable {
>>>>>>   static func ==(lhs: Self, rhs: Self) -> Bool
>>>>>> }
>>>>>>
>>>>>> should be:
>>>>>>
>>>>>> protocol Equatable {
>>>>>>   static func ==(_ lhs: Self, _ rhs: Self) -> Bool
>>>>>> }
>>>>>>
>>>>>
>>>>> Unless I'm mistaken, from looking at the Swift 3 branch of stdlib, the
>>>>> syntax changes don't appear to apply to operator functions. Since they are
>>>>> a special case that don't have argument labels, it wouldn't make sense to
>>>>> require them (or rather, the placeholders) here.
>>>>>
>>>>
>>>> I don't agree with this.
>>>>
>>>> Operators are called like this:
>>>>
>>>> x = y + z
>>>>
>>>> Of course it doesn't make sense to have parameter labels there.
>>>>
>>>> But the ones inside the protocol are not operators. They are methods,
>>>> and are called like methods. They happen to have funny names, but they are
>>>> still methods, and are called like this:
>>>>
>>>> x = T.+(y, z)
>>>>
>>>> In this case not only it makes sense for the parameters to have labels,
>>>> but making them behave differently from normal methods would be
>>>> inconsistent, and a step backwards from all the progress that has been made
>>>> in Swift 3 on that front.
>>>>
>>>
>>> What I'm saying is, if you look at the Swift 3 branch of stdlib, global
>>> operator functions still do not have argument labels. Picking one at
>>> random:
>>> https://github.com/apple/swift/blob/swift-3.0-branch/stdlib/public/core/String.swift#L329
>>>
>>> If you're arguing that those functions should be forced to include `_`
>>> placeholders, that's fine, but it's not accurate to say that the way
>>> they're written in this proposal is a step backwards from all the progress
>>> made in Swift 3. It is *consistent* with the way global operator functions
>>> are currently declared in Swift 3.
>>>
>>> If it changes there, then it should change here as well. But they should
>>> be the same, and making that change for global operator functions is not
>>> part of the scope of this proposal.
>>>
>>
>> I'm not talking about the global operator functions; I'm talking about
>> the methods inside the protocol, which are methods and are called like
>> methods; they are not operators.
>>
>>
> Thanks for expressing this so clearly. I'm of the same feeling but fumbled
> the communication of it.
>
> On re-evaluation, I wonder if this proposal as it is would be a
> sufficiently large improvement. It's essentially permitting the use of
> characters reserved for operators in static method names, but it adds a set
> of somewhat inconsistent rules for how those functions are to be declared
> and called. As mentioned earlier, `T....(x, y)` looks rather unfortunate,
> and since automatic trampolines are out of scope, I wonder if what we have
> currently (naming static methods using words) is altogether that bad. Maybe
> we could just standardize those names and be done with it; on a cursory
> look, that seems to be Rust's approach.
>
>
> 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.
>

Yeah, also good points. Can I propose maybe another approach?

```
T.operator(+, a, b)
T.operator(prefix: ++, a)
T.operator(postfix: ++, a)
```


> 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.
>
>
>
>>
>>
>>>
>>>
>>>
>>>>
>>>>
>>>>>
>>>>>
>>>>>> 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
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160518/17a13403/attachment.html>


More information about the swift-evolution mailing list