Skip to content

Instantly share code, notes, and snippets.

@vmartinelli
Last active November 1, 2018 17:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vmartinelli/67d6ad234c7a4e14f8d5 to your computer and use it in GitHub Desktop.
Save vmartinelli/67d6ad234c7a4e14f8d5 to your computer and use it in GitHub Desktop.
Swift Evolution Proposal: Instance Operators (Feb, 2016)

Instance Operators

Introduction

The proposal aims to allow operator implementation inside 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 {
    let oldValue = x
    x += 1.0
    
    return oldValue
}

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

Allow the operator implementation inside extension/struct/class scope turning these operator funcs into instance funcs.

Detailed design

Protocol conformance

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

// MARK: - protocol

public protocol MyDoubleType {
    public func someUsefulFunction()
    
    public func *(rhs: Self) -> Self
    public mutating postfix func ++() -> Self
}

// MARK: - implementation

extension Double: MyDoubleType {

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

    public func *(rhs: Double) -> Double {
        return self.multipliedBy(rhs)
    }
    
    public mutating postfix func ++() -> Double {
       let oldValue = self
       self += 1.0
       
       return oldValue
    }

}

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 will also 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 greaterThanOrEqualOperator for a >= operator, as example. The operator introduction would be:

infix operator >= {
    associativity none
    precedence 130
    name greaterThanOrEqualOperator
}

So the code will be written like this...

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

... but translated internally to this:

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

And then to _TCF... using the current name mangling system.

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 will continue to be supported without affecting the existing code base.

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 func *(lhs: Self, rhs: Self) -> Self
    public static func /(lhs: Int64, rhs: Int64) -> Int64 // what?
    public static postfix func ++(inout x: Self) -> Self
}

// MARK: - implementation

extension Double: MyDoubleType {

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

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

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

    public static postfix func ++(inout x: Double) -> Double {
        let oldValue = x
        x += 1.0
        
        return oldValue
    }
    
}
@marconvcm
Copy link

Hello from devgrid

@marconvcm
Copy link

This works!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment