[Proposal] Instance Operators


(Vanderlei Martinelli) #1

Hello.

The proposal can be also read at
https://gist.github.com/vmartinelli/67d6ad234c7a4e14f8d5

Original thread:
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160125/008508.html

Opinions, comments and corrections (including on English grammar) are all
welcome. :slight_smile:

-Van

路路路

---------

Instance Operators

聽聽聽- Proposal: SE-NNNN
聽聽聽<https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-instance-operators.md>
聽聽聽- Author: Vanderlei Martinelli <https://github.com/vmartinelli>
聽聽聽- Status: *Awaiting review*
聽聽聽- Review manager: TBD

Introduction

The proposal aims to move operator implementation from the global and
static scope into extension/struct/class instance scope.

Swift-evolution thread: link to the discussion thread for that proposal
<https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160125/008508.html>
Motivation

When writing the protocol interface the operator is declarated inside the
scope of that protocol, but its implementation has to be static and global.
This, besides being inconsistent, might not the behaviour expected by
programmers coming from other languages that have some kind of support for
interface/protocol and operator implementation.

Example:

// MARK: - protocol
public protocol MyDoubleType {
聽聽聽聽public func someUsefulFunction()

聽聽聽聽public func *(lhs: Self, rhs: Self) -> Self
聽聽聽聽public postfix func ++(inout x: Self) -> Self
}
// MARK: - implementation
extension Double: MyDoubleType {

聽聽聽聽public func someUsefulFunction() {
聽聽聽聽聽聽聽聽// ...
聽聽聽聽}

聽聽聽聽// we cannot implement the operators here...

}
// ... but have to implement them here
public func *(lhs: Double, rhs: Double) -> Double {
聽聽聽聽return lhs.multipliedBy(rhs)
}
public postfix func ++(inout x: Double) -> Double {
聽聽聽聽x += 1.0
聽聽聽聽return x
}

Also the current implementation does not leave much room for future
expansion in the use of operators (such as conversion between values, for
example).
Proposed solution

Move the operator implementation into the extension/struct/class scope and
turn operator funcs into instance funcs, using the operator keyword.
Detailed designProtocol conformance

After the change the above code can be written like the example bellow.

// MARK: - protocol
public protocol MyDoubleType {
聽聽聽聽public func someUsefulFunction()

聽聽聽聽public operator *(rhs: Self) -> Self
聽聽聽聽public mutating postfix operator ++() -> Self
}
// MARK: - implementation
extension Double: MyDoubleType {

聽聽聽聽public func someUsefulFunction() {
聽聽聽聽聽聽聽聽// ...
聽聽聽聽}

聽聽聽聽public operator *(rhs: Double) -> Double {
聽聽聽聽聽聽聽聽return self.multipliedBy(rhs)
聽聽聽聽}

聽聽聽聽public mutating postfix operator ++() -> Double {
聽聽聽聽聽聽聽self += 1.0
聽聽聽聽聽聽聽return self
聽聽聽聽}

}

Operator funcs everywhere

An operator does not have to be implemented only to conform to a protocol,
however. It can be also be implemented in any other place where a common
func is. This means that even the current form can be supported.
Operator internal names

Perhaps because of the internal implementation of Swift, operators have to
have names to be handled. The suggestion is to adopt
__operator__GreaterThanOrEqual for a >= operator, as example. The operator
introduction would be:

infix operator >= {
聽聽聽聽associativity none
聽聽聽聽precedence 130
聽聽聽聽name "GreaterThanOrEqual"
}

So the code will be written like this...

struct MyStruct {
聽聽聽聽operator >=(other: MyStruct) -> Bool {
聽聽聽聽聽聽聽聽return ...
聽聽聽聽}
}

... but translated internally to this:

struct MyStruct {
聽聽聽聽func __operator__GreaterThanOrEqual(other: MyStruct) -> Bool {
聽聽聽聽聽聽聽聽return ...
聽聽聽聽}
}

Impact on existing code

Since after this change an operator can be implemented in any other place
where a common func can be, the current implementation may continue to
exist, but marked as deprecated with a compiler/analyser warning.

Also the func keyword would be deprecated for operators as well, using the
operator to declare/implement an operator func.
Alternatives consideredStatus quo

Leave things as they are. Even being inconsistent or not allowing new
possibilities that instance operators will bring.
Static implementation inside extension/struct/class scope

This is the way operators are implemented in C#, for example. The change
would be only aesthetic. The functionality would remain the same as today.

As the types may differ from protocol/structure/class, this would allow
state within the scope of operators that have nothing to do with that type.
Not a good thing. In this case it might be better to keep things as they
are.

Example:

// MARK: - protocol
public protocol MyDoubleType {
聽聽聽聽public func someUsefulFunction()

聽聽聽聽public static operator *(lhs: Self, rhs: Self) -> Self
聽聽聽聽public static operator /(lhs: Int64, rhs: Int64) -> Int64 // what?
聽聽聽聽public static postfix operator ++(inout x: Self) -> Self
}
// MARK: - implementation
extension Double: MyDoubleType {

聽聽聽聽public func someUsefulFunction() {
聽聽聽聽聽聽聽聽// ...
聽聽聽聽}

聽聽聽聽public static operator *(lhs: Double, rhs: Double) -> Double {
聽聽聽聽聽聽聽聽return lhs.multipliedBy(rhs)
聽聽聽聽}

聽聽聽聽// this should be implemented inside a Int64 type, not here...
聽聽聽聽public static operator /(lhs: Int64, rhs: Int64) -> Int64 {
聽聽聽聽聽聽聽聽// ...
聽聽聽聽}

聽聽聽聽public static postfix operator ++(inout x: Double) -> Double {
聽聽聽聽聽聽聽聽x += 1.0
聽聽聽聽聽聽聽聽return x
聽聽聽聽}

}


(David Waite) #2

If operators are not effectively final or static, then the semantics change - operators do not perform dynamic dispatch today, same as functions but different from methods.

see today:

class A :Equatable { }
class B : A { }
func == (lhs:A, rhs:A) -> Bool { return true }
func == (lhs:B, rhs:B) -> Bool { return false }

let b = B()
let a:A = b

a == a // true
b == b // false

-DW

路路路

On Feb 25, 2016, at 12:59 PM, Vanderlei Martinelli via swift-evolution <swift-evolution@swift.org> wrote:

Hello.

The proposal can be also read at https://gist.github.com/vmartinelli/67d6ad234c7a4e14f8d5

Original thread: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160125/008508.html

Opinions, comments and corrections (including on English grammar) are all welcome. :slight_smile:

-Van

---------

Instance Operators

Proposal: SE-NNNN <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-instance-operators.md>
Author: Vanderlei Martinelli <https://github.com/vmartinelli>
Status: Awaiting review
Review manager: TBD
Introduction

The proposal aims to move operator implementation from the global and static scope into extension/struct/class instance scope.

Swift-evolution thread: link to the discussion thread for that proposal <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160125/008508.html>
Motivation

When writing the protocol interface the operator is declarated inside the scope of that protocol, but its implementation has to be static and global. This, besides being inconsistent, might not the behaviour expected by programmers coming from other languages that have some kind of support for interface/protocol and operator implementation.

Example:

// MARK: - protocol

public protocol MyDoubleType {
聽聽聽聽public func someUsefulFunction()

聽聽聽聽public func *(lhs: Self, rhs: Self) -> Self
聽聽聽聽public postfix func ++(inout x: Self) -> Self
}

// MARK: - implementation

extension Double: MyDoubleType {

聽聽聽聽public func someUsefulFunction() {
聽聽聽聽聽聽聽聽// ...
聽聽聽聽}

聽聽聽聽// we cannot implement the operators here...

}

// ... but have to implement them here

public func *(lhs: Double, rhs: Double) -> Double {
聽聽聽聽return lhs.multipliedBy(rhs)
}

public postfix func ++(inout x: Double) -> Double {
聽聽聽聽x += 1.0
聽聽聽聽return x
}
Also the current implementation does not leave much room for future expansion in the use of operators (such as conversion between values, for example).

Proposed solution

Move the operator implementation into the extension/struct/class scope and turn operator funcs into instance funcs, using the operator keyword.

Detailed design

Protocol conformance

After the change the above code can be written like the example bellow.

// MARK: - protocol

public protocol MyDoubleType {
聽聽聽聽public func someUsefulFunction()

聽聽聽聽public operator *(rhs: Self) -> Self
聽聽聽聽public mutating postfix operator ++() -> Self
}

// MARK: - implementation

extension Double: MyDoubleType {

聽聽聽聽public func someUsefulFunction() {
聽聽聽聽聽聽聽聽// ...
聽聽聽聽}

聽聽聽聽public operator *(rhs: Double) -> Double {
聽聽聽聽聽聽聽聽return self.multipliedBy(rhs)
聽聽聽聽}

聽聽聽聽public mutating postfix operator ++() -> Double {
聽聽聽聽聽聽聽self += 1.0
聽聽聽聽聽聽聽return self
聽聽聽聽}

}
Operator funcs everywhere

An operator does not have to be implemented only to conform to a protocol, however. It can be also be implemented in any other place where a common func is. This means that even the current form can be supported.

Operator internal names

Perhaps because of the internal implementation of Swift, operators have to have names to be handled. The suggestion is to adopt __operator__GreaterThanOrEqual for a >= operator, as example. The operator introduction would be:

infix operator >= {
聽聽聽聽associativity none
聽聽聽聽precedence 130
聽聽聽聽name "GreaterThanOrEqual"
}
So the code will be written like this...

struct MyStruct {
聽聽聽聽operator >=(other: MyStruct) -> Bool {
聽聽聽聽聽聽聽聽return ...
聽聽聽聽}
}
... but translated internally to this:

struct MyStruct {
聽聽聽聽func __operator__GreaterThanOrEqual(other: MyStruct) -> Bool {
聽聽聽聽聽聽聽聽return ...
聽聽聽聽}
}
Impact on existing code

Since after this change an operator can be implemented in any other place where a common func can be, the current implementation may continue to exist, but marked as deprecated with a compiler/analyser warning.

Also the func keyword would be deprecated for operators as well, using the operator to declare/implement an operator func.

Alternatives considered

Status quo

Leave things as they are. Even being inconsistent or not allowing new possibilities that instance operators will bring.

Static implementation inside extension/struct/class scope

This is the way operators are implemented in C#, for example. The change would be only aesthetic. The functionality would remain the same as today.

As the types may differ from protocol/structure/class, this would allow state within the scope of operators that have nothing to do with that type. Not a good thing. In this case it might be better to keep things as they are.

Example:

// MARK: - protocol

public protocol MyDoubleType {
聽聽聽聽public func someUsefulFunction()

聽聽聽聽public static operator *(lhs: Self, rhs: Self) -> Self
聽聽聽聽public static operator /(lhs: Int64, rhs: Int64) -> Int64 // what?
聽聽聽聽public static postfix operator ++(inout x: Self) -> Self
}

// MARK: - implementation

extension Double: MyDoubleType {

聽聽聽聽public func someUsefulFunction() {
聽聽聽聽聽聽聽聽// ...
聽聽聽聽}

聽聽聽聽public static operator *(lhs: Double, rhs: Double) -> Double {
聽聽聽聽聽聽聽聽return lhs.multipliedBy(rhs)
聽聽聽聽}

聽聽聽聽// this should be implemented inside a Int64 type, not here...
聽聽聽聽public static operator /(lhs: Int64, rhs: Int64) -> Int64 {
聽聽聽聽聽聽聽聽// ...
聽聽聽聽}

聽聽聽聽public static postfix operator ++(inout x: Double) -> Double {
聽聽聽聽聽聽聽聽x += 1.0
聽聽聽聽聽聽聽聽return x
聽聽聽聽}

}

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Jordan Rose) #3

I think this is a direction we want to go in, but how does this work for unary operators with a non-object type, and binary operators with a non-object LHS?

As DavidW said, it's important to figure out what this means for classes. (Don't forget asymmetrical operations within a class hierarchy, i.e. `A() == B()` vs. `B() == A()`.)

I also think you should subset out the change from "func" to "operator". Currently "func" and "operator" mean very different things, and discussing that here is just going to muddy your intent.

Jordan

路路路

On Feb 25, 2016, at 11:59, Vanderlei Martinelli via swift-evolution <swift-evolution@swift.org> wrote:

Hello.

The proposal can be also read at https://gist.github.com/vmartinelli/67d6ad234c7a4e14f8d5

Original thread: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160125/008508.html

Opinions, comments and corrections (including on English grammar) are all welcome. :slight_smile:

-Van

---------

Instance Operators

Proposal: SE-NNNN <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-instance-operators.md>
Author: Vanderlei Martinelli <https://github.com/vmartinelli>
Status: Awaiting review
Review manager: TBD
Introduction

The proposal aims to move operator implementation from the global and static scope into extension/struct/class instance scope.

Swift-evolution thread: link to the discussion thread for that proposal <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160125/008508.html>
Motivation

When writing the protocol interface the operator is declarated inside the scope of that protocol, but its implementation has to be static and global. This, besides being inconsistent, might not the behaviour expected by programmers coming from other languages that have some kind of support for interface/protocol and operator implementation.

Example:

// MARK: - protocol

public protocol MyDoubleType {
聽聽聽聽public func someUsefulFunction()

聽聽聽聽public func *(lhs: Self, rhs: Self) -> Self
聽聽聽聽public postfix func ++(inout x: Self) -> Self
}

// MARK: - implementation

extension Double: MyDoubleType {

聽聽聽聽public func someUsefulFunction() {
聽聽聽聽聽聽聽聽// ...
聽聽聽聽}

聽聽聽聽// we cannot implement the operators here...

}

// ... but have to implement them here

public func *(lhs: Double, rhs: Double) -> Double {
聽聽聽聽return lhs.multipliedBy(rhs)
}

public postfix func ++(inout x: Double) -> Double {
聽聽聽聽x += 1.0
聽聽聽聽return x
}
Also the current implementation does not leave much room for future expansion in the use of operators (such as conversion between values, for example).

Proposed solution

Move the operator implementation into the extension/struct/class scope and turn operator funcs into instance funcs, using the operator keyword.

Detailed design

Protocol conformance

After the change the above code can be written like the example bellow.

// MARK: - protocol

public protocol MyDoubleType {
聽聽聽聽public func someUsefulFunction()

聽聽聽聽public operator *(rhs: Self) -> Self
聽聽聽聽public mutating postfix operator ++() -> Self
}

// MARK: - implementation

extension Double: MyDoubleType {

聽聽聽聽public func someUsefulFunction() {
聽聽聽聽聽聽聽聽// ...
聽聽聽聽}

聽聽聽聽public operator *(rhs: Double) -> Double {
聽聽聽聽聽聽聽聽return self.multipliedBy(rhs)
聽聽聽聽}

聽聽聽聽public mutating postfix operator ++() -> Double {
聽聽聽聽聽聽聽self += 1.0
聽聽聽聽聽聽聽return self
聽聽聽聽}

}
Operator funcs everywhere

An operator does not have to be implemented only to conform to a protocol, however. It can be also be implemented in any other place where a common func is. This means that even the current form can be supported.

Operator internal names

Perhaps because of the internal implementation of Swift, operators have to have names to be handled. The suggestion is to adopt __operator__GreaterThanOrEqual for a >= operator, as example. The operator introduction would be:

infix operator >= {
聽聽聽聽associativity none
聽聽聽聽precedence 130
聽聽聽聽name "GreaterThanOrEqual"
}
So the code will be written like this...

struct MyStruct {
聽聽聽聽operator >=(other: MyStruct) -> Bool {
聽聽聽聽聽聽聽聽return ...
聽聽聽聽}
}
... but translated internally to this:

struct MyStruct {
聽聽聽聽func __operator__GreaterThanOrEqual(other: MyStruct) -> Bool {
聽聽聽聽聽聽聽聽return ...
聽聽聽聽}
}
Impact on existing code

Since after this change an operator can be implemented in any other place where a common func can be, the current implementation may continue to exist, but marked as deprecated with a compiler/analyser warning.

Also the func keyword would be deprecated for operators as well, using the operator to declare/implement an operator func.

Alternatives considered

Status quo

Leave things as they are. Even being inconsistent or not allowing new possibilities that instance operators will bring.

Static implementation inside extension/struct/class scope

This is the way operators are implemented in C#, for example. The change would be only aesthetic. The functionality would remain the same as today.

As the types may differ from protocol/structure/class, this would allow state within the scope of operators that have nothing to do with that type. Not a good thing. In this case it might be better to keep things as they are.

Example:

// MARK: - protocol

public protocol MyDoubleType {
聽聽聽聽public func someUsefulFunction()

聽聽聽聽public static operator *(lhs: Self, rhs: Self) -> Self
聽聽聽聽public static operator /(lhs: Int64, rhs: Int64) -> Int64 // what?
聽聽聽聽public static postfix operator ++(inout x: Self) -> Self
}

// MARK: - implementation

extension Double: MyDoubleType {

聽聽聽聽public func someUsefulFunction() {
聽聽聽聽聽聽聽聽// ...
聽聽聽聽}

聽聽聽聽public static operator *(lhs: Double, rhs: Double) -> Double {
聽聽聽聽聽聽聽聽return lhs.multipliedBy(rhs)
聽聽聽聽}

聽聽聽聽// this should be implemented inside a Int64 type, not here...
聽聽聽聽public static operator /(lhs: Int64, rhs: Int64) -> Int64 {
聽聽聽聽聽聽聽聽// ...
聽聽聽聽}

聽聽聽聽public static postfix operator ++(inout x: Double) -> Double {
聽聽聽聽聽聽聽聽x += 1.0
聽聽聽聽聽聽聽聽return x
聽聽聽聽}

}

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Haravikk) #4

I鈥檓 a big +1 for the proposal, I hate managing operators the way they are now. In fact I tend to implement Equatable and Comparable with .equals() and .compareTo() methods in my types so that I only have to call those in the operator implementations (which coincidentally can avoid the problem you mention):

路路路

On 25 Feb 2016, at 20:11, David Waite via swift-evolution <swift-evolution@swift.org> wrote:

If operators are not effectively final or static, then the semantics change - operators do not perform dynamic dispatch today, same as functions but different from methods.

An important point, but this proposal does suggest keeping the current method as well for the time being, so we can have the instance form use dynamic dispatch like any other method. Personally though I think that making the transition is important as the lack of dynamic dispatch is something that can be confusing and lead to hard to find bugs; in your example the bug is pretty obvious to track down, but in more detailed implementations it may not be obvious that the type of your value is changing the behaviour for operators when it doesn鈥檛 for methods.


(Haravikk) #5

I think this is a direction we want to go in, but how does this work for unary operators with a non-object type, and binary operators with a non-object LHS?

These could still use the global form of the operator; actually that does raise the point though that perhaps it shouldn鈥檛 be deprecated as suggested in the proposal, but instead we should just encourage the instance form for structs and classes?

As DavidW said, it's important to figure out what this means for classes. (Don't forget asymmetrical operations within a class hierarchy, i.e. `A() == B()` vs. `B() == A()`.)

How big a problem is this in practice? Java for example just requires you to provide an equals() method, and actually that method takes an argument of Java鈥檚 Object type which is even more vacuous, but I never really found it to be problematic. In fact, thinking back on it I鈥檓 surprised that I didn鈥檛.

Could we possibly provide some means of making an operator flippable? i.e- if in your example B extends A, the compiler would ensure that B, the more specific type, is always on the left hand side? Otherwise, while we can use is to test inheritance from a particular type, is there a way to test whether something is a sub-class? For example, we could implement operators such as:

聽聽class Foo {
聽聽聽聽func == (rhs: Foo) -> Bool {
聽聽聽聽聽聽if rhs isSubClassOf Foo { return rhs == self }
聽聽聽聽聽聽...
聽聽聽聽}
聽聽}

Is that currently possible or would it require a new feature? Thought that would be better if the comparison could be made against Self (as sub-classes that don鈥檛 override the operator could produce horrible loops).

路路路

On 25 Feb 2016, at 23:11, Jordan Rose via swift-evolution <swift-evolution@swift.org> wrote:


(plx) #6

After careful consideration, I am not sure this sort of thing is actually a change that would *actually* be an obvious improvement.

# First Concern: Code-Organization/Ownership Issues

Although it is easy to see how to handle operators that have *homogenous* types 鈥 e.g. anything like `func +(lhs: T, rhs: T) -> T` 鈥 it鈥檚 really unclear how a proposal like this *should* work for operators that have *heterogeneous* types 鈥 e.g. anything like `func *(lhs: T, rhs: U) -> U ` (etc.).

Since we鈥檙e talking about operators, this isn鈥檛 really a hypothetical concern, either:

## Example: Vector/Matrix Operations

func *(lhs: Matrix4x4, rhs: Vector4) -> Vector4
func *(lhs: Vector4, rhs: Matrix4x4) -> Vector4

Both operations are reasonable to define, but defining the operator as instance methods seems to leave you in a rather awkward spot:

- perhaps one is implemented by `Matrix4x4` and the other by `Vector4` (really odd code organization imho鈥)
- perhaps both are implemented by, say, `Matrix4x4`, but one of them is using nonstandard syntax (defeating the point of custom operators, imho)
- perhaps the proposal lets an operator-function declaration indicate *which* argument is `self` (new syntax/new semantics)

鈥hereas the 鈥渙perators are static functions鈥 approach makes it reasonable to have both versions defined at the same scope (and e.g. "near each other鈥).

I know the specific proposal here wouldn鈥檛 eliminate the ability to define the operators as currently, but it鈥檇 be a shame to be unable to include things like the above method in protocols.

## Example: Scalar/Vector Operations

Similarly, at a more-basic level, you might *want* this:

protocol VectorType : Equatable {
聽聽typealias Component : NumericType // if it existed

聽聽// to match convention, scalars go *in front* of vectors
聽聽operator *(lhs: Int, rhs: Self) -> Self
聽聽operator *(lhs: Self.Component, rhs: Self) -> Self

聽聽// but why should we not be flexible on this point?
聽聽operator *(lhs: Self, rhs: Int) -> Self
聽聽operator *(lhs: Self, rhs: Self.Component) -> Self
}

鈥nd are we going to make `struct Vector4`鈥檚 conformance to `VectorType` contingent on the presence of extension methods on `Int` (etc.)?

That just seems really unintuitive and counterproductive.

# Second Concern: Dynamic Operator Dispatch Not Really Necessary

What I mean is, in the use cases that come to mind for dynamic operator-dispatch. making operators dynamically-dispatched wouldnt鈥 actually provide any benefit over what you can achieve today with protocols.

EG, for `==`, consider a protocol and operators like below:

protocol DynamicEquatable : class {
聽聽func isEqual(other: Self) -> Bool
}

func ==<T:DynamicEquatable>(lhs: T, rhs: T) -> Bool {
聽聽return (lhs === rhs) || lhs.isEqual(rhs)
}

func !=<T:DynamicEquatable>(lhs: T, rhs: T) -> Bool {
聽聽return (lhs !== rhs) && !lhs.isEqual(rhs)
}

鈥hich as far as I can tell gets you back to the same place you鈥檇 be if you had a dynamically-dispatched `==` (both in terms of *what it would do* and also *what issues it would still have*).

Is there some (beneficial?) aspect of making `==` dynamically-dispatched that isn鈥檛 also present in the above design?

Are there operators for which there would be a material difference between the operator being dynamically-dispatched and the operator being defined over a protocol that has a dynamically-dispatched method providing the implementation?

# Remark

For `==` in particular, you could *maybe* improve the above slightly if Swift had a way to write a where clause like `U:>T` 鈥 meaning 鈥淯 is a subclass of T, but not T itself鈥 鈥 as then you could add variants like:

func ==<T:DynamicEquatable,U:DynamicEquatable where T:>U>(lhs: T, rhs: U) -> Bool {
聽聽return (lhs === rhs) || lhs.isEqual(rhs)
}

func ==<T:DynamicEquatable,U:DynamicEquatable where U:>T>(lhs: T, rhs: U) -> Bool {
聽聽return (lhs === rhs) || rhs.isEqual(lhs)
}

鈥hich would hopefully make a direct call into the more-derived type鈥檚 implementation of `isEqual`, which would be more-likely to contain a fast path, but even so it鈥檚 not obvious that there鈥檚 all that much of an actual win to be had here, in practice.

路路路

On Feb 25, 2016, at 1:59 PM, Vanderlei Martinelli via swift-evolution <swift-evolution@swift.org> wrote:

Hello.

The proposal can be also read at https://gist.github.com/vmartinelli/67d6ad234c7a4e14f8d5

Original thread: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160125/008508.html

Opinions, comments and corrections (including on English grammar) are all welcome. :slight_smile:

-Van

---------

Instance Operators

Proposal: SE-NNNN <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-instance-operators.md>
Author: Vanderlei Martinelli <https://github.com/vmartinelli>
Status: Awaiting review
Review manager: TBD
Introduction

The proposal aims to move operator implementation from the global and static scope into extension/struct/class instance scope.

Swift-evolution thread: link to the discussion thread for that proposal <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160125/008508.html>
Motivation

When writing the protocol interface the operator is declarated inside the scope of that protocol, but its implementation has to be static and global. This, besides being inconsistent, might not the behaviour expected by programmers coming from other languages that have some kind of support for interface/protocol and operator implementation.

Example:

// MARK: - protocol

public protocol MyDoubleType {
聽聽聽聽public func someUsefulFunction()

聽聽聽聽public func *(lhs: Self, rhs: Self) -> Self
聽聽聽聽public postfix func ++(inout x: Self) -> Self
}

// MARK: - implementation

extension Double: MyDoubleType {

聽聽聽聽public func someUsefulFunction() {
聽聽聽聽聽聽聽聽// ...
聽聽聽聽}

聽聽聽聽// we cannot implement the operators here...

}

// ... but have to implement them here

public func *(lhs: Double, rhs: Double) -> Double {
聽聽聽聽return lhs.multipliedBy(rhs)
}

public postfix func ++(inout x: Double) -> Double {
聽聽聽聽x += 1.0
聽聽聽聽return x
}
Also the current implementation does not leave much room for future expansion in the use of operators (such as conversion between values, for example).

Proposed solution

Move the operator implementation into the extension/struct/class scope and turn operator funcs into instance funcs, using the operator keyword.

Detailed design

Protocol conformance

After the change the above code can be written like the example bellow.

// MARK: - protocol

public protocol MyDoubleType {
聽聽聽聽public func someUsefulFunction()

聽聽聽聽public operator *(rhs: Self) -> Self
聽聽聽聽public mutating postfix operator ++() -> Self
}

// MARK: - implementation

extension Double: MyDoubleType {

聽聽聽聽public func someUsefulFunction() {
聽聽聽聽聽聽聽聽// ...
聽聽聽聽}

聽聽聽聽public operator *(rhs: Double) -> Double {
聽聽聽聽聽聽聽聽return self.multipliedBy(rhs)
聽聽聽聽}

聽聽聽聽public mutating postfix operator ++() -> Double {
聽聽聽聽聽聽聽self += 1.0
聽聽聽聽聽聽聽return self
聽聽聽聽}

}
Operator funcs everywhere

An operator does not have to be implemented only to conform to a protocol, however. It can be also be implemented in any other place where a common func is. This means that even the current form can be supported.

Operator internal names

Perhaps because of the internal implementation of Swift, operators have to have names to be handled. The suggestion is to adopt __operator__GreaterThanOrEqual for a >= operator, as example. The operator introduction would be:

infix operator >= {
聽聽聽聽associativity none
聽聽聽聽precedence 130
聽聽聽聽name "GreaterThanOrEqual"
}
So the code will be written like this...

struct MyStruct {
聽聽聽聽operator >=(other: MyStruct) -> Bool {
聽聽聽聽聽聽聽聽return ...
聽聽聽聽}
}
... but translated internally to this:

struct MyStruct {
聽聽聽聽func __operator__GreaterThanOrEqual(other: MyStruct) -> Bool {
聽聽聽聽聽聽聽聽return ...
聽聽聽聽}
}
Impact on existing code

Since after this change an operator can be implemented in any other place where a common func can be, the current implementation may continue to exist, but marked as deprecated with a compiler/analyser warning.

Also the func keyword would be deprecated for operators as well, using the operator to declare/implement an operator func.

Alternatives considered

Status quo

Leave things as they are. Even being inconsistent or not allowing new possibilities that instance operators will bring.

Static implementation inside extension/struct/class scope

This is the way operators are implemented in C#, for example. The change would be only aesthetic. The functionality would remain the same as today.

As the types may differ from protocol/structure/class, this would allow state within the scope of operators that have nothing to do with that type. Not a good thing. In this case it might be better to keep things as they are.

Example:

// MARK: - protocol

public protocol MyDoubleType {
聽聽聽聽public func someUsefulFunction()

聽聽聽聽public static operator *(lhs: Self, rhs: Self) -> Self
聽聽聽聽public static operator /(lhs: Int64, rhs: Int64) -> Int64 // what?
聽聽聽聽public static postfix operator ++(inout x: Self) -> Self
}

// MARK: - implementation

extension Double: MyDoubleType {

聽聽聽聽public func someUsefulFunction() {
聽聽聽聽聽聽聽聽// ...
聽聽聽聽}

聽聽聽聽public static operator *(lhs: Double, rhs: Double) -> Double {
聽聽聽聽聽聽聽聽return lhs.multipliedBy(rhs)
聽聽聽聽}

聽聽聽聽// this should be implemented inside a Int64 type, not here...
聽聽聽聽public static operator /(lhs: Int64, rhs: Int64) -> Int64 {
聽聽聽聽聽聽聽聽// ...
聽聽聽聽}

聽聽聽聽public static postfix operator ++(inout x: Double) -> Double {
聽聽聽聽聽聽聽聽x += 1.0
聽聽聽聽聽聽聽聽return x
聽聽聽聽}

}

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Nicola Salmoria) #7

I鈥檓 wondering if we might be trying to solve the wrong problem here.

Let鈥檚 look for example at part of the definition of the IntegerArithmeticType protocol (disregarding implementation details):

public protocol IntegerArithmeticType {

聽聽聽聽public func +(lhs: Self, rhs: Self) -> Self

聽聽聽聽public static func addWithOverflow(lhs: Self, _ rhs: Self) -> (Self, overflow: Bool)
}

There鈥檚 an obvious asymmetry here: + is defined as 鈥減ublic func鈥, while addWithOverflow is 鈥減ublic static func鈥.

Perhaps instead of extending how operators are defined, it would be more efficient to reconsider how protocol conformance should be defined.
It seems to me that a 鈥榮wifter鈥 way to do the above would be

public protocol IntegerArithmeticType {

聽聽聽聽public func add(to: Self) -> Self

聽聽聽聽public func addWithOverflow(to: Self) -> (Self, overflow: Bool)
}

and then generic operators can simply be defined once at global scope:

public func +<T: IntegerArithmeticType>(lhs: T, rhs: T) -> T {
聽聽return lhs.add(hrs)
}

public func &+<T: IntegerArithmeticType>(lhs: T, rhs: T) -> T {
聽聽return lhs.addWithOverflow(hrs).0
}

Equatable and Comparable have similar asymmetries:

/// Instances of conforming types can be compared for value equality
/// using operators `==` and `!=`.
///
/// When adopting `Equatable`, only the `==` operator is required to be
/// implemented. The standard library provides an implementation for `!=`.
public protocol Equatable {
聽聽聽聽public func ==(lhs: Self, rhs: Self) -> Bool
}

so we need to define ==, but not != because it鈥檚 derived from the former.
Again, the cause of the inconsistency is the requirement to define the operator as part of the protocol conformance. If it was done like this, it might seem simpler and more symmetrical:

public protocol Equatable {
聽聽聽聽public func equals(other: Self) -> Bool
}

public func ==<T: Equatable>(lhs: T, rhs: T) -> T {
聽聽return lhs.equals(hrs)
}
public func !=<T: Equatable>(lhs: T, rhs: T) -> T {
聽聽return !lhs.equals(hrs)
}

Nicola

路路路

Hello.

The proposal can be also read athttps://gist.github.com/vmartinelli/67d6ad234c7a4e14f8d5

Original thread:https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160125/008508.html

Opinions, comments and corrections (including on English grammar) are all welcome. :slight_smile:

-Van

---------

Instance Operators
Proposal:SE-NNNN(https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-instance-operators.md)
Author:Vanderlei Martinelli(https://github.com/vmartinelli)
Status:Awaiting review
Review manager: TBD

Introduction

The proposal aims to move operator implementation from the global and static scope into extension/struct/class instance scope.

Swift-evolution thread:link to the discussion thread for that proposal(https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160125/008508.html)

Motivation

When writing the protocol interface the operator is declarated inside the scope of that protocol, but its implementation has to be static and global. This, besides being inconsistent, might not the behaviour expected by programmers coming from other languages that have some kind of support for interface/protocol and operator implementation.

Example:

// MARK: - protocolpublicprotocolMyDoubleType {publicfuncsomeUsefulFunction()publicfunc*(lhs:Self, rhs:Self)->Selfpublicpostfixfunc++(inoutx:Self)->Self }// MARK: - implementationextensionDouble: MyDoubleType {publicfuncsomeUsefulFunction() {// ...}// we cannot implement the operators here...}// ... but have to implement them herepublicfunc*(lhs:Double, rhs:Double)->Double{returnlhs.multipliedBy(rhs) }publicpostfixfunc++(inoutx:Double)->Double{ x+=1.0returnx }

Also the current implementation does not leave much room for future expansion in the use of operators (such as conversion between values, for example).

Proposed solution

Move the operator implementation into the extension/struct/class scope and turn operator funcs into instance funcs, using theoperatorkeyword.

Detailed design
Protocol conformance

After the change the above code can be written like the example bellow.

// MARK: - protocolpublicprotocolMyDoubleType {publicfuncsomeUsefulFunction()publicoperator*(rhs:Self)->Selfpublicmutatingpostfixoperator++()->Self}// MARK: - implementationextensionDouble: MyDoubleType {publicfuncsomeUsefulFunction() {// ...}publicoperator*(rhs:Double)->Double{returnself.multipliedBy(rhs) }publicmutatingpostfixoperator++()->Double{self+=1.0returnself} }

Operator funcs everywhere

An operator does not have to be implemented only to conform to a protocol, however. It can be also be implemented in any other place where a common func is. This means that even the current form can be supported.

Operator internal names

Perhaps because of the internal implementation of Swift, operators have to have names to be handled. The suggestion is to adopt__operator__GreaterThanOrEqualfor a>=operator, as example. The operator introduction would be:

>={associativitynoneprecedence130name"GreaterThanOrEqual"}

So the code will be written like this...

structMyStruct {operator>=(other: MyStruct)->Bool{return...} }

... but translated internally to this:

structMyStruct {func__operator__GreaterThanOrEqual(other: MyStruct)->Bool{return...} }

Impact on existing code

Since after this change an operator can be implemented in any other place where a common func can be, the current implementation may continue to exist, but marked as deprecated with a compiler/analyser warning.

Also thefunckeyword would be deprecated for operators as well, using theoperatorto declare/implement an operator func.

Alternatives considered
Status quo

Leave things as they are. Even being inconsistent or not allowing new possibilities that instance operators will bring.

Static implementation inside extension/struct/class scope

This is the way operators are implemented in C#, for example. The change would be only aesthetic. The functionality would remain the same as today.

As the types may differ from protocol/structure/class, this would allow state within the scope of operators that have nothing to do with that type. Not a good thing. In this case it might be better to keep things as they are.

Example:

// MARK: - protocolpublicprotocolMyDoubleType {publicfuncsomeUsefulFunction()publicstaticoperator*(lhs:Self, rhs:Self)->Selfpublicstaticoperator/(lhs:Int64, rhs:Int64)->Int64// what?publicstaticpostfixoperator++(inoutx:Self)->Self}// MARK: - implementationextensionDouble: MyDoubleType {publicfuncsomeUsefulFunction() {// ...}publicstaticoperator*(lhs:Double, rhs:Double)->Double{returnlhs.multipliedBy(rhs) }// this should be implemented inside a Int64 type, not here...publicstaticoperator/(lhs:Int64, rhs:Int64)->Int64{// ...}publicstaticpostfixoperator++(inoutx:Double)->Double{ x+=1.0returnx } }


(Vanderlei Martinelli) #8

Thank you for your feedback. My comments are below:

I think this is a direction we want to go in, but how does this work for
unary operators with a non-object type, and binary operators with a
non-object LHS?

I'd like to see some examples regarding this.

As DavidW said, it's important to figure out what this means for classes.
(Don't forget asymmetrical operations within a class hierarchy, i.e. `A()
== B()` vs. `B() == A()`.)

I think that currently the same problem can happen or am I mistaken?

I also think you should subset out the change from "func" to "operator".
Currently "func" and "operator" mean very different things, and discussing
that here is just going to muddy your intent.

I updated the proposal draft (
https://gist.github.com/vmartinelli/67d6ad234c7a4e14f8d5) to remove the
change from `func` to `operator` keyword. Also removed the deprecation of
the current pattern.

Jordan

-Van

路路路

On Thu, Feb 25, 2016 at 8:11 PM, Jordan Rose <jordan_rose@apple.com> wrote:


(Sean Heber) #9

I agree with all of this. I鈥檓 not sure operators really belong *inside* of the type. IMO, attempting to include them within the type is only desirable because of the way they are declared inside of the protocol. I think the asymmetry should be addressed on that side instead (if at all).

l8r
Sean

路路路

On Feb 26, 2016, at 10:43 AM, plx via swift-evolution <swift-evolution@swift.org> wrote:

After careful consideration, I am not sure this sort of thing is actually a change that would *actually* be an obvious improvement.

# First Concern: Code-Organization/Ownership Issues

Although it is easy to see how to handle operators that have *homogenous* types 鈥 e.g. anything like `func +(lhs: T, rhs: T) -> T` 鈥 it鈥檚 really unclear how a proposal like this *should* work for operators that have *heterogeneous* types 鈥 e.g. anything like `func *(lhs: T, rhs: U) -> U ` (etc.).

Since we鈥檙e talking about operators, this isn鈥檛 really a hypothetical concern, either:

## Example: Vector/Matrix Operations

func *(lhs: Matrix4x4, rhs: Vector4) -> Vector4
func *(lhs: Vector4, rhs: Matrix4x4) -> Vector4

Both operations are reasonable to define, but defining the operator as instance methods seems to leave you in a rather awkward spot:

- perhaps one is implemented by `Matrix4x4` and the other by `Vector4` (really odd code organization imho鈥)
- perhaps both are implemented by, say, `Matrix4x4`, but one of them is using nonstandard syntax (defeating the point of custom operators, imho)
- perhaps the proposal lets an operator-function declaration indicate *which* argument is `self` (new syntax/new semantics)

鈥hereas the 鈥渙perators are static functions鈥 approach makes it reasonable to have both versions defined at the same scope (and e.g. "near each other鈥).

I know the specific proposal here wouldn鈥檛 eliminate the ability to define the operators as currently, but it鈥檇 be a shame to be unable to include things like the above method in protocols.

## Example: Scalar/Vector Operations

Similarly, at a more-basic level, you might *want* this:

protocol VectorType : Equatable {
聽聽typealias Component : NumericType // if it existed

聽聽// to match convention, scalars go *in front* of vectors
聽聽operator *(lhs: Int, rhs: Self) -> Self
聽聽operator *(lhs: Self.Component, rhs: Self) -> Self

聽聽// but why should we not be flexible on this point?
聽聽operator *(lhs: Self, rhs: Int) -> Self
聽聽operator *(lhs: Self, rhs: Self.Component) -> Self
}

鈥nd are we going to make `struct Vector4`鈥檚 conformance to `VectorType` contingent on the presence of extension methods on `Int` (etc.)?

That just seems really unintuitive and counterproductive.

# Second Concern: Dynamic Operator Dispatch Not Really Necessary

What I mean is, in the use cases that come to mind for dynamic operator-dispatch. making operators dynamically-dispatched wouldnt鈥 actually provide any benefit over what you can achieve today with protocols.

EG, for `==`, consider a protocol and operators like below:

protocol DynamicEquatable : class {
聽聽func isEqual(other: Self) -> Bool
}

func ==<T:DynamicEquatable>(lhs: T, rhs: T) -> Bool {
聽聽return (lhs === rhs) || lhs.isEqual(rhs)
}

func !=<T:DynamicEquatable>(lhs: T, rhs: T) -> Bool {
聽聽return (lhs !== rhs) && !lhs.isEqual(rhs)
}

鈥hich as far as I can tell gets you back to the same place you鈥檇 be if you had a dynamically-dispatched `==` (both in terms of *what it would do* and also *what issues it would still have*).

Is there some (beneficial?) aspect of making `==` dynamically-dispatched that isn鈥檛 also present in the above design?

Are there operators for which there would be a material difference between the operator being dynamically-dispatched and the operator being defined over a protocol that has a dynamically-dispatched method providing the implementation?

# Remark

For `==` in particular, you could *maybe* improve the above slightly if Swift had a way to write a where clause like `U:>T` 鈥 meaning 鈥淯 is a subclass of T, but not T itself鈥 鈥 as then you could add variants like:

func ==<T:DynamicEquatable,U:DynamicEquatable where T:>U>(lhs: T, rhs: U) -> Bool {
聽聽return (lhs === rhs) || lhs.isEqual(rhs)
}

func ==<T:DynamicEquatable,U:DynamicEquatable where U:>T>(lhs: T, rhs: U) -> Bool {
聽聽return (lhs === rhs) || rhs.isEqual(lhs)
}

鈥hich would hopefully make a direct call into the more-derived type鈥檚 implementation of `isEqual`, which would be more-likely to contain a fast path, but even so it鈥檚 not obvious that there鈥檚 all that much of an actual win to be had here, in practice.

On Feb 25, 2016, at 1:59 PM, Vanderlei Martinelli via swift-evolution <swift-evolution@swift.org> wrote:

Hello.

The proposal can be also read at https://gist.github.com/vmartinelli/67d6ad234c7a4e14f8d5

Original thread: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160125/008508.html

Opinions, comments and corrections (including on English grammar) are all welcome. :slight_smile:

-Van

---------

Instance Operators

聽聽鈥 Proposal: SE-NNNN
聽聽鈥 Author: Vanderlei Martinelli
聽聽鈥 Status: Awaiting review
聽聽鈥 Review manager: TBD
Introduction

The proposal aims to move operator implementation from the global and static scope into extension/struct/class instance scope.

Swift-evolution thread: link to the discussion thread for that proposal

Motivation

When writing the protocol interface the operator is declarated inside the scope of that protocol, but its implementation has to be static and global. This, besides being inconsistent, might not the behaviour expected by programmers coming from other languages that have some kind of support for interface/protocol and operator implementation.

Example:

// MARK: - protocol

public protocol
MyDoubleType {
聽聽聽聽
public func someUsefulFunction
()

public func *(lhs: Self, rhs: Self) ->
Self
聽聽聽聽
public postfix func ++(inout x: Self) ->
Self
}

// MARK: - implementation

extension Double
: MyDoubleType {

public func someUsefulFunction
() {
聽聽聽聽聽聽聽聽
// ...

聽聽聽聽}

// we cannot implement the operators here...

}

// ... but have to implement them here

public func *(lhs: Double, rhs: Double) -> Double
{
聽聽聽聽
return lhs.
multipliedBy(rhs)
}

public postfix func ++(inout x: Double) -> Double
{
聽聽聽聽x
+= 1.0

return
x
}

Also the current implementation does not leave much room for future expansion in the use of operators (such as conversion between values, for example).

Proposed solution

Move the operator implementation into the extension/struct/class scope and turn operator funcs into instance funcs, using the operator keyword.

Detailed design

Protocol conformance

After the change the above code can be written like the example bellow.

// MARK: - protocol

public protocol
MyDoubleType {
聽聽聽聽
public func someUsefulFunction
()

public operator *(rhs: Self) -> Self

public mutating postfix operator ++() -> Self

}

// MARK: - implementation

extension Double
: MyDoubleType {

public func someUsefulFunction
() {
聽聽聽聽聽聽聽聽
// ...

聽聽聽聽}

public operator *(rhs: Double) -> Double
{
聽聽聽聽聽聽聽聽
return self.
multipliedBy(rhs)
聽聽聽聽}

public mutating postfix operator ++() -> Double
{
聽聽聽聽聽聽聽
self += 1.0

return self

聽聽聽聽}

}

Operator funcs everywhere

An operator does not have to be implemented only to conform to a protocol, however. It can be also be implemented in any other place where a common func is. This means that even the current form can be supported.

Operator internal names

Perhaps because of the internal implementation of Swift, operators have to have names to be handled. The suggestion is to adopt __operator__GreaterThanOrEqual for a >= operator, as example. The operator introduction would be:

infix operator >=
{
聽聽聽聽
associativity none

precedence 130

聽聽聽聽name
"GreaterThanOrEqual"

}

So the code will be written like this...

struct
MyStruct {
聽聽聽聽
operator >=(other: MyStruct) -> Bool
{
聽聽聽聽聽聽聽聽
return ...

聽聽聽聽}
}

... but translated internally to this:

struct
MyStruct {
聽聽聽聽
func __operator__GreaterThanOrEqual(other: MyStruct) -> Bool
{
聽聽聽聽聽聽聽聽
return ...

聽聽聽聽}
}

Impact on existing code

Since after this change an operator can be implemented in any other place where a common func can be, the current implementation may continue to exist, but marked as deprecated with a compiler/analyser warning.

Also the func keyword would be deprecated for operators as well, using the operator to declare/implement an operator func.

Alternatives considered

Status quo

Leave things as they are. Even being inconsistent or not allowing new possibilities that instance operators will bring.

Static implementation inside extension/struct/class scope

This is the way operators are implemented in C#, for example. The change would be only aesthetic. The functionality would remain the same as today.

As the types may differ from protocol/structure/class, this would allow state within the scope of operators that have nothing to do with that type. Not a good thing. In this case it might be better to keep things as they are.

Example:

// MARK: - protocol

public protocol
MyDoubleType {
聽聽聽聽
public func someUsefulFunction
()

public static operator *(lhs: Self, rhs: Self) -> Self

public static operator /(lhs: Int64, rhs: Int64) -> Int64 // what?

public static postfix operator ++(inout x: Self) -> Self

}

// MARK: - implementation

extension Double
: MyDoubleType {

public func someUsefulFunction
() {
聽聽聽聽聽聽聽聽
// ...

聽聽聽聽}

public static operator *(lhs: Double, rhs: Double) -> Double
{
聽聽聽聽聽聽聽聽
return lhs.
multipliedBy(rhs)
聽聽聽聽}

// this should be implemented inside a Int64 type, not here...

public static operator /(lhs: Int64, rhs: Int64) -> Int64
{
聽聽聽聽聽聽聽聽
// ...

聽聽聽聽}

public static postfix operator ++(inout x: Double) -> Double
{
聽聽聽聽聽聽聽聽x
+= 1.0

return
x
聽聽聽聽}

}

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Vanderlei Martinelli) #10

Thanks for the comments and suggestions.

A new text for the proposal is available at
https://gist.github.com/vmartinelli/67d6ad234c7a4e14f8d5

We can continue the discussion from there.

-Van


(Thorsten Seitz) #11

I don't think symmetric operators should be mapped to asymmetric methods which are only dispatched on the receiver's type.

Of course the current scheme using global functions with no dynamic dispatch at all is not much better (the selected function depends on the static types I happen to see at the call site but at least it isn't preferring one of the arguments.
Having real multiple dispatch would be nice :slight_smile:

-Thorsten

路路路

Am 26.02.2016 um 11:41 schrieb Nicola Salmoria via swift-evolution <swift-evolution@swift.org>:

I鈥檓 wondering if we might be trying to solve the wrong problem here.

Let鈥檚 look for example at part of the definition of the IntegerArithmeticType protocol (disregarding implementation details):

public protocol IntegerArithmeticType {

聽聽聽聽public func +(lhs: Self, rhs: Self) -> Self

聽聽聽聽public static func addWithOverflow(lhs: Self, _ rhs: Self) -> (Self, overflow: Bool)
}

There鈥檚 an obvious asymmetry here: + is defined as 鈥減ublic func鈥, while addWithOverflow is 鈥減ublic static func鈥.

Perhaps instead of extending how operators are defined, it would be more efficient to reconsider how protocol conformance should be defined.
It seems to me that a 鈥榮wifter鈥 way to do the above would be

public protocol IntegerArithmeticType {

聽聽聽聽public func add(to: Self) -> Self

聽聽聽聽public func addWithOverflow(to: Self) -> (Self, overflow: Bool)
}

and then generic operators can simply be defined once at global scope:

public func +<T: IntegerArithmeticType>(lhs: T, rhs: T) -> T {
聽聽return lhs.add(hrs)
}

public func &+<T: IntegerArithmeticType>(lhs: T, rhs: T) -> T {
聽聽return lhs.addWithOverflow(hrs).0
}

Equatable and Comparable have similar asymmetries:

/// Instances of conforming types can be compared for value equality
/// using operators `==` and `!=`.
///
/// When adopting `Equatable`, only the `==` operator is required to be
/// implemented. The standard library provides an implementation for `!=`.
public protocol Equatable {
聽聽聽聽public func ==(lhs: Self, rhs: Self) -> Bool
}

so we need to define ==, but not != because it鈥檚 derived from the former.
Again, the cause of the inconsistency is the requirement to define the operator as part of the protocol conformance. If it was done like this, it might seem simpler and more symmetrical:

public protocol Equatable {
聽聽聽聽public func equals(other: Self) -> Bool
}

public func ==<T: Equatable>(lhs: T, rhs: T) -> T {
聽聽return lhs.equals(hrs)
}
public func !=<T: Equatable>(lhs: T, rhs: T) -> T {
聽聽return !lhs.equals(hrs)
}

Nicola

> Hello.
>
> The proposal can be also read athttps://gist.github.com/vmartinelli/67d6ad234c7a4e14f8d5
>
> Original thread:https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160125/008508.html
>
> Opinions, comments and corrections (including on English grammar) are all welcome. :slight_smile:
>
> -Van
>
> ---------
>
> Instance Operators
> Proposal:SE-NNNN(https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-instance-operators.md)
> Author:Vanderlei Martinelli(https://github.com/vmartinelli)
> Status:Awaiting review
> Review manager: TBD
>
> Introduction
>
> The proposal aims to move operator implementation from the global and static scope into extension/struct/class instance scope.
>
>
> Swift-evolution thread:link to the discussion thread for that proposal(https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160125/008508.html)
>
> Motivation
>
> When writing the protocol interface the operator is declarated inside the scope of that protocol, but its implementation has to be static and global. This, besides being inconsistent, might not the behaviour expected by programmers coming from other languages that have some kind of support for interface/protocol and operator implementation.
>
>
> Example:
>
> // MARK: - protocolpublicprotocolMyDoubleType {publicfuncsomeUsefulFunction()publicfunc*(lhs:Self, rhs:Self)->Selfpublicpostfixfunc++(inoutx:Self)->Self }// MARK: - implementationextensionDouble: MyDoubleType {publicfuncsomeUsefulFunction() {// ...}// we cannot implement the operators here...}// ... but have to implement them herepublicfunc*(lhs:Double, rhs:Double)->Double{returnlhs.multipliedBy(rhs) }publicpostfixfunc++(inoutx:Double)->Double{ x+=1.0returnx }
>
>
> Also the current implementation does not leave much room for future expansion in the use of operators (such as conversion between values, for example).
>
> Proposed solution
>
> Move the operator implementation into the extension/struct/class scope and turn operator funcs into instance funcs, using theoperatorkeyword.
>
> Detailed design
> Protocol conformance
>
> After the change the above code can be written like the example bellow.
>
> // MARK: - protocolpublicprotocolMyDoubleType {publicfuncsomeUsefulFunction()publicoperator*(rhs:Self)->Selfpublicmutatingpostfixoperator++()->Self}// MARK: - implementationextensionDouble: MyDoubleType {publicfuncsomeUsefulFunction() {// ...}publicoperator*(rhs:Double)->Double{returnself.multipliedBy(rhs) }publicmutatingpostfixoperator++()->Double{self+=1.0returnself} }
>
> Operator funcs everywhere
>
> An operator does not have to be implemented only to conform to a protocol, however. It can be also be implemented in any other place where a common func is. This means that even the current form can be supported.
>
> Operator internal names
>
> Perhaps because of the internal implementation of Swift, operators have to have names to be handled. The suggestion is to adopt__operator__GreaterThanOrEqualfor a>=operator, as example. The operator introduction would be:
>
> >={associativitynoneprecedence130name"GreaterThanOrEqual"}
>
>
> So the code will be written like this...
>
> structMyStruct {operator>=(other: MyStruct)->Bool{return...} }
>
>
> ... but translated internally to this:
>
> structMyStruct {func__operator__GreaterThanOrEqual(other: MyStruct)->Bool{return...} }
>
> Impact on existing code
>
> Since after this change an operator can be implemented in any other place where a common func can be, the current implementation may continue to exist, but marked as deprecated with a compiler/analyser warning.
>
>
> Also thefunckeyword would be deprecated for operators as well, using theoperatorto declare/implement an operator func.
>
> Alternatives considered
> Status quo
>
> Leave things as they are. Even being inconsistent or not allowing new possibilities that instance operators will bring.
>
> Static implementation inside extension/struct/class scope
>
> This is the way operators are implemented in C#, for example. The change would be only aesthetic. The functionality would remain the same as today.
>
>
> As the types may differ from protocol/structure/class, this would allow state within the scope of operators that have nothing to do with that type. Not a good thing. In this case it might be better to keep things as they are.
>
>
> Example:
>
> // MARK: - protocolpublicprotocolMyDoubleType {publicfuncsomeUsefulFunction()publicstaticoperator*(lhs:Self, rhs:Self)->Selfpublicstaticoperator/(lhs:Int64, rhs:Int64)->Int64// what?publicstaticpostfixoperator++(inoutx:Self)->Self}// MARK: - implementationextensionDouble: MyDoubleType {publicfuncsomeUsefulFunction() {// ...}publicstaticoperator*(lhs:Double, rhs:Double)->Double{returnlhs.multipliedBy(rhs) }// this should be implemented inside a Int64 type, not here...publicstaticoperator/(lhs:Int64, rhs:Int64)->Int64{// ...}publicstaticpostfixoperator++(inoutx:Double)->Double{ x+=1.0returnx } }
>
>
>
>
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Vanderlei Martinelli) #12

I mean with "from there": "from the current proposal text".

-Van

路路路

On Fri, Feb 26, 2016 at 12:45 PM, Vanderlei Martinelli < vmartinelli@alecrim.com> wrote:

Thanks for the comments and suggestions.

A new text for the proposal is available at
https://gist.github.com/vmartinelli/67d6ad234c7a4e14f8d5

We can continue the discussion from there.

-Van


(Nicola Salmoria) #13

If the argument symmetry should be preserved, I think I'd still prefer to
leave operators out of this, and consistently use static methods, like this:

public protocol IntegerArithmeticType {
聽聽聽聽public static func add(lhs: Self, _ rhs: Self) -> Self
聽聽聽聽public static func addWithOverflow(lhs: Self, _ rhs: Self) -> (Self,
overflow: Bool)
}

public func +<T: IntegerArithmeticType>(lhs: T, rhs: T) -> T {
聽聽聽聽return T.add(lhs, rhs)
}
public func &+<T: IntegerArithmeticType>(lhs: T, rhs: T) -> T {
聽聽聽聽return T.addWithOverflow(lhs, rhs).0
}

public protocol Equatable {
聽聽聽聽public static func equal(lhs: Self, _ rhs: Self) -> Bool
}

public func ==<T: Equatable>(lhs: T, rhs: T) -> T {
聽聽聽聽return T.equal(lhs, rhs)
}
public func !=<T: Equatable>(lhs: T, rhs: T) -> T {
聽聽聽聽return !T.equals(lhs, rhs)
}

路路路

--
Nicola

On Fri, Feb 26, 2016 at 4:18 PM, Thorsten Seitz <tseitz42@icloud.com> wrote:

I don't think symmetric operators should be mapped to asymmetric methods
which are only dispatched on the receiver's type.

Of course the current scheme using global functions with no dynamic
dispatch at all is not much better (the selected function depends on the
static types I happen to see at the call site but at least it isn't
preferring one of the arguments.
Having real multiple dispatch would be nice :slight_smile:

-Thorsten

Am 26.02.2016 um 11:41 schrieb Nicola Salmoria via swift-evolution < > swift-evolution@swift.org>:

I鈥檓 wondering if we might be trying to solve the wrong problem here.

Let鈥檚 look for example at part of the definition of the
IntegerArithmeticType protocol (disregarding implementation details):

public protocol IntegerArithmeticType {

聽聽聽聽public func +(lhs: Self, rhs: Self) -> Self

聽聽聽聽public static func addWithOverflow(lhs: Self, _ rhs: Self) -> (Self,
overflow: Bool)
}

There鈥檚 an obvious asymmetry here: + is defined as 鈥減ublic func鈥, while
addWithOverflow is 鈥減ublic static func鈥.

Perhaps instead of extending how operators are defined, it would be more
efficient to reconsider how protocol conformance should be defined.
It seems to me that a 鈥榮wifter鈥 way to do the above would be

public protocol IntegerArithmeticType {

聽聽聽聽public func add(to: Self) -> Self

聽聽聽聽public func addWithOverflow(to: Self) -> (Self, overflow: Bool)
}

and then generic operators can simply be defined once at global scope:

public func +<T: IntegerArithmeticType>(lhs: T, rhs: T) -> T {
return lhs.add(hrs)
}

public func &+<T: IntegerArithmeticType>(lhs: T, rhs: T) -> T {
return lhs.addWithOverflow(hrs).0
}

Equatable and Comparable have similar asymmetries:

/// Instances of conforming types can be compared for value equality
/// using operators `==` and `!=`.
///
/// When adopting `Equatable`, only the `==` operator is required to be
/// implemented. The standard library provides an implementation for `!=`.
public protocol Equatable {
聽聽聽聽public func ==(lhs: Self, rhs: Self) -> Bool
}

so we need to define ==, but not != because it鈥檚 derived from the former.
Again, the cause of the inconsistency is the requirement to define the
operator as part of the protocol conformance. If it was done like this, it
might seem simpler and more symmetrical:

public protocol Equatable {
聽聽聽聽public func equals(other: Self) -> Bool
}

public func ==<T: Equatable>(lhs: T, rhs: T) -> T {
return lhs.equals(hrs)
}
public func !=<T: Equatable>(lhs: T, rhs: T) -> T {
return !lhs.equals(hrs)
}

Nicola

> Hello.
>
> The proposal can be also read
athttps://gist.github.com/vmartinelli/67d6ad234c7a4e14f8d5
>
> Original thread:https://
lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160125/008508.html
>
> Opinions, comments and corrections (including on English grammar) are
all welcome. :slight_smile:
>
> -Van
>
> ---------
>
> Instance Operators
> Proposal:SE-NNNN(
https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-instance-operators.md
)
> Author:Vanderlei Martinelli(https://github.com/vmartinelli)
> Status:Awaiting review
> Review manager: TBD
>
> Introduction
>
> The proposal aims to move operator implementation from the global and
static scope into extension/struct/class instance scope.
>
>
> Swift-evolution thread:link to the discussion thread for that proposal(
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160125/008508.html
)
>
> Motivation
>
> When writing the protocol interface the operator is declarated inside
the scope of that protocol, but its implementation has to be static and
global. This, besides being inconsistent, might not the behaviour expected
by programmers coming from other languages that have some kind of support
for interface/protocol and operator implementation.
>
>
> Example:
>
> // MARK: - protocolpublicprotocolMyDoubleType
{publicfuncsomeUsefulFunction()publicfunc*(lhs:Self,
rhs:Self)->Selfpublicpostfixfunc++(inoutx:Self)->Self }// MARK: -
implementationextensionDouble: MyDoubleType {publicfuncsomeUsefulFunction()
{// ...}// we cannot implement the operators here...}// ... but have to
implement them herepublicfunc*(lhs:Double,
rhs:Double)->Double{returnlhs.multipliedBy(rhs)
}publicpostfixfunc++(inoutx:Double)->Double{ x+=1.0returnx }
>
>
> Also the current implementation does not leave much room for future
expansion in the use of operators (such as conversion between values, for
example).
>
> Proposed solution
>
> Move the operator implementation into the extension/struct/class scope
and turn operator funcs into instance funcs, using theoperatorkeyword.
>
> Detailed design
> Protocol conformance
>
> After the change the above code can be written like the example bellow.
>
> // MARK: - protocolpublicprotocolMyDoubleType
{publicfuncsomeUsefulFunction()publicoperator*(rhs:Self)->Selfpublicmutatingpostfixoperator++()->Self}//
MARK: - implementationextensionDouble: MyDoubleType
{publicfuncsomeUsefulFunction() {//
...}publicoperator*(rhs:Double)->Double{returnself.multipliedBy(rhs)
}publicmutatingpostfixoperator++()->Double{self+=1.0returnself} }
>
> Operator funcs everywhere
>
> An operator does not have to be implemented only to conform to a
protocol, however. It can be also be implemented in any other place where a
common func is. This means that even the current form can be supported.
>
> Operator internal names
>
> Perhaps because of the internal implementation of Swift, operators have
to have names to be handled. The suggestion is to
adopt__operator__GreaterThanOrEqualfor a>=operator, as example. The
operator introduction would be:
>
> >={associativitynoneprecedence130name"GreaterThanOrEqual"}
>
>
> So the code will be written like this...
>
> structMyStruct {operator>=(other: MyStruct)->Bool{return...} }
>
>
> ... but translated internally to this:
>
> structMyStruct {func__operator__GreaterThanOrEqual(other:
MyStruct)->Bool{return...} }
>
> Impact on existing code
>
> Since after this change an operator can be implemented in any other
place where a common func can be, the current implementation may continue
to exist, but marked as deprecated with a compiler/analyser warning.
>
>
> Also thefunckeyword would be deprecated for operators as well, using
theoperatorto declare/implement an operator func.
>
> Alternatives considered
> Status quo
>
> Leave things as they are. Even being inconsistent or not allowing new
possibilities that instance operators will bring.
>
> Static implementation inside extension/struct/class scope
>
> This is the way operators are implemented in C#, for example. The change
would be only aesthetic. The functionality would remain the same as today.
>
>
> As the types may differ from protocol/structure/class, this would allow
state within the scope of operators that have nothing to do with that type.
Not a good thing. In this case it might be better to keep things as they
are.
>
>
> Example:
>
> // MARK: - protocolpublicprotocolMyDoubleType
{publicfuncsomeUsefulFunction()publicstaticoperator*(lhs:Self,
rhs:Self)->Selfpublicstaticoperator/(lhs:Int64, rhs:Int64)->Int64//
what?publicstaticpostfixoperator++(inoutx:Self)->Self}// MARK: -
implementationextensionDouble: MyDoubleType {publicfuncsomeUsefulFunction()
{// ...}publicstaticoperator*(lhs:Double,
rhs:Double)->Double{returnlhs.multipliedBy(rhs) }// this should be
implemented inside a Int64 type, not
here...publicstaticoperator/(lhs:Int64, rhs:Int64)->Int64{//
...}publicstaticpostfixoperator++(inoutx:Double)->Double{
x+=1.0returnx } }
>
>
>
>

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Thorsten Seitz) #14

I agree with all of this. I鈥檓 not sure operators really belong *inside* of the type. IMO, attempting to include them within the type is only desirable because of the way they are declared inside of the protocol. I think the asymmetry should be addressed on that side instead (if at all).

Good point! Maybe we should think about turning operators into multi-methods which are dynamically dispatched on all arguments like Dylan had. Or at least just move their declaration out of protocols.

-Thorsten

路路路

Am 26.02.2016 um 17:55 schrieb Sean Heber via swift-evolution <swift-evolution@swift.org>:

l8r
Sean

On Feb 26, 2016, at 10:43 AM, plx via swift-evolution <swift-evolution@swift.org> wrote:

After careful consideration, I am not sure this sort of thing is actually a change that would *actually* be an obvious improvement.

# First Concern: Code-Organization/Ownership Issues

Although it is easy to see how to handle operators that have *homogenous* types 鈥 e.g. anything like `func +(lhs: T, rhs: T) -> T` 鈥 it鈥檚 really unclear how a proposal like this *should* work for operators that have *heterogeneous* types 鈥 e.g. anything like `func *(lhs: T, rhs: U) -> U ` (etc.).

Since we鈥檙e talking about operators, this isn鈥檛 really a hypothetical concern, either:

## Example: Vector/Matrix Operations

func *(lhs: Matrix4x4, rhs: Vector4) -> Vector4
func *(lhs: Vector4, rhs: Matrix4x4) -> Vector4

Both operations are reasonable to define, but defining the operator as instance methods seems to leave you in a rather awkward spot:

- perhaps one is implemented by `Matrix4x4` and the other by `Vector4` (really odd code organization imho鈥)
- perhaps both are implemented by, say, `Matrix4x4`, but one of them is using nonstandard syntax (defeating the point of custom operators, imho)
- perhaps the proposal lets an operator-function declaration indicate *which* argument is `self` (new syntax/new semantics)

鈥hereas the 鈥渙perators are static functions鈥 approach makes it reasonable to have both versions defined at the same scope (and e.g. "near each other鈥).

I know the specific proposal here wouldn鈥檛 eliminate the ability to define the operators as currently, but it鈥檇 be a shame to be unable to include things like the above method in protocols.

## Example: Scalar/Vector Operations

Similarly, at a more-basic level, you might *want* this:

protocol VectorType : Equatable {
typealias Component : NumericType // if it existed

// to match convention, scalars go *in front* of vectors
operator *(lhs: Int, rhs: Self) -> Self
operator *(lhs: Self.Component, rhs: Self) -> Self

// but why should we not be flexible on this point?
operator *(lhs: Self, rhs: Int) -> Self
operator *(lhs: Self, rhs: Self.Component) -> Self
}

鈥nd are we going to make `struct Vector4`鈥檚 conformance to `VectorType` contingent on the presence of extension methods on `Int` (etc.)?

That just seems really unintuitive and counterproductive.

# Second Concern: Dynamic Operator Dispatch Not Really Necessary

What I mean is, in the use cases that come to mind for dynamic operator-dispatch. making operators dynamically-dispatched wouldnt鈥 actually provide any benefit over what you can achieve today with protocols.

EG, for `==`, consider a protocol and operators like below:

protocol DynamicEquatable : class {
func isEqual(other: Self) -> Bool
}

func ==<T:DynamicEquatable>(lhs: T, rhs: T) -> Bool {
return (lhs === rhs) || lhs.isEqual(rhs)
}

func !=<T:DynamicEquatable>(lhs: T, rhs: T) -> Bool {
return (lhs !== rhs) && !lhs.isEqual(rhs)
}

鈥hich as far as I can tell gets you back to the same place you鈥檇 be if you had a dynamically-dispatched `==` (both in terms of *what it would do* and also *what issues it would still have*).

Is there some (beneficial?) aspect of making `==` dynamically-dispatched that isn鈥檛 also present in the above design?

Are there operators for which there would be a material difference between the operator being dynamically-dispatched and the operator being defined over a protocol that has a dynamically-dispatched method providing the implementation?

# Remark

For `==` in particular, you could *maybe* improve the above slightly if Swift had a way to write a where clause like `U:>T` 鈥 meaning 鈥淯 is a subclass of T, but not T itself鈥 鈥 as then you could add variants like:

func ==<T:DynamicEquatable,U:DynamicEquatable where T:>U>(lhs: T, rhs: U) -> Bool {
return (lhs === rhs) || lhs.isEqual(rhs)
}

func ==<T:DynamicEquatable,U:DynamicEquatable where U:>T>(lhs: T, rhs: U) -> Bool {
return (lhs === rhs) || rhs.isEqual(lhs)
}

鈥hich would hopefully make a direct call into the more-derived type鈥檚 implementation of `isEqual`, which would be more-likely to contain a fast path, but even so it鈥檚 not obvious that there鈥檚 all that much of an actual win to be had here, in practice.


(Andrew Bennett) #15

I'm +1 on Nicola's suggestions.

Basically, remove operators from protocols, simplifying the language. This
doesn't prevent operators from using protocols (through generics).

Operators with protocols can still be defined as Nicola suggested, through
a regular protocol method and a generic. Symmetrical operators can use a
static method on the protocol if that's desirable. Swift has all the
functionality for this already.

It also doesn't have the downside of preventing operators from working on
things like types, tuples, etc.

路路路

On Mon, Feb 29, 2016 at 5:24 PM, Thorsten Seitz via swift-evolution < swift-evolution@swift.org> wrote:

> Am 26.02.2016 um 17:55 schrieb Sean Heber via swift-evolution < > swift-evolution@swift.org>:
>
> I agree with all of this. I鈥檓 not sure operators really belong *inside*
of the type. IMO, attempting to include them within the type is only
desirable because of the way they are declared inside of the protocol. I
think the asymmetry should be addressed on that side instead (if at all).

Good point! Maybe we should think about turning operators into
multi-methods which are dynamically dispatched on all arguments like Dylan
had. Or at least just move their declaration out of protocols.

-Thorsten

>
> l8r
> Sean
>
>
>
>> On Feb 26, 2016, at 10:43 AM, plx via swift-evolution < > swift-evolution@swift.org> wrote:
>>
>> After careful consideration, I am not sure this sort of thing is
actually a change that would *actually* be an obvious improvement.
>>
>> # First Concern: Code-Organization/Ownership Issues
>>
>> Although it is easy to see how to handle operators that have
*homogenous* types 鈥 e.g. anything like `func +(lhs: T, rhs: T) -> T` 鈥
it鈥檚 really unclear how a proposal like this *should* work for operators
that have *heterogeneous* types 鈥 e.g. anything like `func *(lhs: T, rhs:
U) -> U ` (etc.).
>>
>> Since we鈥檙e talking about operators, this isn鈥檛 really a hypothetical
concern, either:
>>
>> ## Example: Vector/Matrix Operations
>>
>> func *(lhs: Matrix4x4, rhs: Vector4) -> Vector4
>> func *(lhs: Vector4, rhs: Matrix4x4) -> Vector4
>>
>> Both operations are reasonable to define, but defining the operator as
instance methods seems to leave you in a rather awkward spot:
>>
>> - perhaps one is implemented by `Matrix4x4` and the other by `Vector4`
(really odd code organization imho鈥)
>> - perhaps both are implemented by, say, `Matrix4x4`, but one of them is
using nonstandard syntax (defeating the point of custom operators, imho)
>> - perhaps the proposal lets an operator-function declaration indicate
*which* argument is `self` (new syntax/new semantics)
>>
>> 鈥hereas the 鈥渙perators are static functions鈥 approach makes it
reasonable to have both versions defined at the same scope (and e.g. "near
each other鈥).
>>
>> I know the specific proposal here wouldn鈥檛 eliminate the ability to
define the operators as currently, but it鈥檇 be a shame to be unable to
include things like the above method in protocols.
>>
>> ## Example: Scalar/Vector Operations
>>
>> Similarly, at a more-basic level, you might *want* this:
>>
>> protocol VectorType : Equatable {
>> typealias Component : NumericType // if it existed
>>
>> // to match convention, scalars go *in front* of vectors
>> operator *(lhs: Int, rhs: Self) -> Self
>> operator *(lhs: Self.Component, rhs: Self) -> Self
>>
>> // but why should we not be flexible on this point?
>> operator *(lhs: Self, rhs: Int) -> Self
>> operator *(lhs: Self, rhs: Self.Component) -> Self
>> }
>>
>> 鈥nd are we going to make `struct Vector4`鈥檚 conformance to
`VectorType` contingent on the presence of extension methods on `Int`
(etc.)?
>>
>> That just seems really unintuitive and counterproductive.
>>
>> # Second Concern: Dynamic Operator Dispatch Not Really Necessary
>>
>> What I mean is, in the use cases that come to mind for dynamic
operator-dispatch. making operators dynamically-dispatched wouldnt鈥
actually provide any benefit over what you can achieve today with protocols.
>>
>> EG, for `==`, consider a protocol and operators like below:
>>
>> protocol DynamicEquatable : class {
>> func isEqual(other: Self) -> Bool
>> }
>>
>> func ==<T:DynamicEquatable>(lhs: T, rhs: T) -> Bool {
>> return (lhs === rhs) || lhs.isEqual(rhs)
>> }
>>
>> func !=<T:DynamicEquatable>(lhs: T, rhs: T) -> Bool {
>> return (lhs !== rhs) && !lhs.isEqual(rhs)
>> }
>>
>> 鈥hich as far as I can tell gets you back to the same place you鈥檇 be if
you had a dynamically-dispatched `==` (both in terms of *what it would do*
and also *what issues it would still have*).
>>
>> Is there some (beneficial?) aspect of making `==`
dynamically-dispatched that isn鈥檛 also present in the above design?
>>
>> Are there operators for which there would be a material difference
between the operator being dynamically-dispatched and the operator being
defined over a protocol that has a dynamically-dispatched method providing
the implementation?
>>
>> # Remark
>>
>> For `==` in particular, you could *maybe* improve the above slightly if
Swift had a way to write a where clause like `U:>T` 鈥 meaning 鈥淯 is a
subclass of T, but not T itself鈥 鈥 as then you could add variants like:
>>
>> func ==<T:DynamicEquatable,U:DynamicEquatable where T:>U>(lhs: T, rhs:
U) -> Bool {
>> return (lhs === rhs) || lhs.isEqual(rhs)
>> }
>>
>> func ==<T:DynamicEquatable,U:DynamicEquatable where U:>T>(lhs: T, rhs:
U) -> Bool {
>> return (lhs === rhs) || rhs.isEqual(lhs)
>> }
>>
>> 鈥hich would hopefully make a direct call into the more-derived type鈥檚
implementation of `isEqual`, which would be more-likely to contain a fast
path, but even so it鈥檚 not obvious that there鈥檚 all that much of an actual
win to be had here, in practice.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(David Sweeris) #16

Why not do it the other way around, like this?
func == <T: Equatable> (lhs: T, rhs: T) -> Bool {
聽聽return T.equals(lhs, rhs)
}

路路路

On Mar 4, 2016, at 3:40 PM, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

...
This should be relatively simple to implement since in many ways we鈥檙e actually just getting rid of a feature, though of course the impact on code is more complex and probably not an easy one to convert automatically (as it would mean somehow pulling global == declarations into a local .equals() method and similar on affected types).

But I think in the long run it鈥檚 a cleaner system, and offers all the flexibility we need.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Haravikk) #17

Having thought about it I think that this a good summary of my thoughts now too; by having protocols like Equatable specify method requirements instead of operators we can get the best of both worlds without having to introduce a new syntax for specifying operators as instance-specific constructs.

This should be relatively simple to implement since in many ways we鈥檙e actually just getting rid of a feature, though of course the impact on code is more complex and probably not an easy one to convert automatically (as it would mean somehow pulling global == declarations into a local .equals() method and similar on affected types).

But I think in the long run it鈥檚 a cleaner system, and offers all the flexibility we need.

路路路

On 4 Mar 2016, at 12:54, Andrew Bennett via swift-evolution <swift-evolution@swift.org> wrote:

I'm +1 on Nicola's suggestions.

Basically, remove operators from protocols, simplifying the language. This doesn't prevent operators from using protocols (through generics).

Operators with protocols can still be defined as Nicola suggested, through a regular protocol method and a generic. Symmetrical operators can use a static method on the protocol if that's desirable. Swift has all the functionality for this already.

It also doesn't have the downside of preventing operators from working on things like types, tuples, etc.


(Haravikk) #18

Actually that鈥檚 what I was referring to; the difficulty is how to convert existing implementations of an equals operator into the new format, which means we鈥檇 probably have to just remove it from Equatable, require an equals() method then force developers to update their code.

i.e- if someone already has:

聽聽func == (lhs:MyType, rhs:MyType) -> Bool { /* Do some stuff */ }

It鈥檚 not clear whether it would be safe just covert that into an equals() method, removing the lhs parameter and replacing it with references to self instead. It might just be one of those changes that has to break code, but will be better in the long-run.

路路路

On 4 Mar 2016, at 21:53, davesweeris@mac.com wrote:

Why not do it the other way around, like this?
func == <T: Equatable> (lhs: T, rhs: T) -> Bool {
聽聽return T.equals(lhs, rhs)
}

On Mar 4, 2016, at 3:40 PM, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

...
This should be relatively simple to implement since in many ways we鈥檙e actually just getting rid of a feature, though of course the impact on code is more complex and probably not an easy one to convert automatically (as it would mean somehow pulling global == declarations into a local .equals() method and similar on affected types).

But I think in the long run it鈥檚 a cleaner system, and offers all the flexibility we need.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Adrian Kashivskyy) #19

I like the idea of moving operator implementations to the inner scope of a type, but I think this proposal doesn't cover all that is needed.

My main concern are infix operators 鈥 your proposal assumes that infix operators will always be implemented with the source on the left-hand-side:

public operator * (rhs: Self) -> Self

My second concern is the internal operator symbol names:

__operator__GreaterThanOrEqual

How should the compiler generate the name for below operator if it translates `>=` to `greater than or equal`?

infix operator <**$$**>>

Pozdrawiam 鈥 Regards,
Adrian Kashivskyy

路路路

Wiadomo艣膰 napisana przez Haravikk via swift-evolution <swift-evolution@swift.org> w dniu 04.03.2016, o godz. 23:49:

Actually that鈥檚 what I was referring to; the difficulty is how to convert existing implementations of an equals operator into the new format, which means we鈥檇 probably have to just remove it from Equatable, require an equals() method then force developers to update their code.

i.e- if someone already has:

聽聽func == (lhs:MyType, rhs:MyType) -> Bool { /* Do some stuff */ }

It鈥檚 not clear whether it would be safe just covert that into an equals() method, removing the lhs parameter and replacing it with references to self instead. It might just be one of those changes that has to break code, but will be better in the long-run.

On 4 Mar 2016, at 21:53, davesweeris@mac.com wrote:

Why not do it the other way around, like this?
func == <T: Equatable> (lhs: T, rhs: T) -> Bool {
聽聽return T.equals(lhs, rhs)
}

On Mar 4, 2016, at 3:40 PM, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

...
This should be relatively simple to implement since in many ways we鈥檙e actually just getting rid of a feature, though of course the impact on code is more complex and probably not an easy one to convert automatically (as it would mean somehow pulling global == declarations into a local .equals() method and similar on affected types).

But I think in the long run it鈥檚 a cleaner system, and offers all the flexibility we need.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution