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

Nicola Salmoria nicola.salmoria at gmail.com
Wed May 18 15:37:13 CDT 2016


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.



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


More information about the swift-evolution mailing list