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

Tony Allevato allevato at google.com
Wed May 18 13:03:19 CDT 2016

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).

> 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.

> 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. (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.

> 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/45b8b4b1/attachment.html>

More information about the swift-evolution mailing list