Clarifying questions about Swift's approach to user-defined operators

Hi Swift Community —

I'm very much a non-Swift expert, though I'm very excited about its design the more I learn about it. I'm currently learning about how Swift supports operators on user-defined types via type methods, such as the example from the "Operator Methods" section of the Advanced Operators section of the Swift Language Guide:

struct Vector2D {
    var x = 0.0, y = 0.0
}

extension Vector2D {
    static func + (left: Vector2D, right: Vector2D) -> Vector2D {
        return Vector2D(x: left.x + right.x, y: left.y + right.y)
    }
}

As I understand it, though this is defined as a static/type method, it is different from typical static/type methods in that one can simply write myVector2D + myOtherVector2D rather than Vector2D.+(myVector2D, myOtherVector2D). Am I correct about that?

If so, I was curious whether someone who's been involved in the development of the Swift language could provide some rationale or context for Swift taking this approach (which I find intriguing and attractive, but would like to understand better).

My guess is that it has the advantages of (a) permitting operator overloads to appear more like normal/standalone functions while (b) permitting them to be associated with specific types rather than being free functions. Or maybe using Python as a point of comparison, that is permits operators to be defined as methods without making this into one of the two operands and opening up problems related to the "reverse" versions of methods when the LHS argument is a distinct type (e.g., int + Vector2D). And that these benefits were considered to outweigh any potential weirdness around having a type method that wasn't explicitly called on a type. But this is all just guesswork on my part, and I'd be interested in hearing from the experts (or to be pointed to design discussions around this, if they are public).

Thanks very much,
-Brad

I believe that originally operators were required to be global (i.e. top-level) functions, with special call syntax.
However this lead to problems with the UI of protocols and type checker performance:

[SE-0091]: Improving operator requirements in protocols:

When a type conforms to a protocol that declares an operator as a requirement, that operator must be implemented as a global function defined outside of the conforming type. This can lead both to user confusion and to poor type checker performance since the global namespace is overcrowded with a large number of operator overloads. This proposal mitigates both of those issues by proposing that operators in protocols be declared statically (to change and clarify where the conforming type implements it) and that Swift use universal lookup for operators that finds candidates both at the global scope and within types.

5 Likes

@Dante-Broggi

Thanks very much for the quick response and pointer to SE-0091, which is exactly what I was hoping for; it seems to confirm many of my guesses.

If you (or others reading this) know: Are there other type methods in Swift that can be invoked without applying them to a type name, or are operators unique in this regard? (I'm guessing this pattern is unique to operators?).

Thanks again,
-Brad

One follow-up to this that may be of use to others reading this topic in the future: A colleague points out that I'd missed that Swift also permits operators to be defined as standalone functions—they only have to be type methods to be part of a protocol. For example:

struct MyStructType {
    var x = 0;
}

func +(a: MyStructType, b: MyStructType) -> MyStructType {
    return MyStructType(x:a.x + b.x);
}

var a: MyStructType = MyStructType(x:1);
var b: MyStructType = MyStructType(x:2);
var c: MyStructType = a + b;


print(a)
print(b)
print(c)

I'd missed this in my scan of the language guide.