Polymorphic behavior for overloaded == (and other) operators


(Frederick Kellison-Linn) #1

Currently, implementing an ‘==‘ function for a custom class can be somewhat cumbersome. In the simple hierarchy:

class A {
    var a: Int
    init(_ a: Int) {
        self.a = a
    }
}

class B : A {
    var b: Int
    init(_ b: Int, _ a: Int) {
        self.b = b
        super.init(a)
    }
}

A reasonable implementation of == would seem to be

func ==(lhs: A, rhs: A) -> Bool {
    return lhs.a == rhs.a
}

func ==(lhs: B, rhs: B) -> Bool {
    return lhs.a == rhs.a &&
           lhs.b == rhs.b
}

However, this fails in the case that the static type of compared variables differs from their dynamic type. E.g:

let x = A(3)
let y: A = B(3, 4)

x == y // true

The immediately obvious solution is to add a check to every == implementation that may need to be implemented for a subtype:

func ==(lhs: A, rhs: A) -> Bool {
    if (lhs.dynamicType != rhs.dynamicType) {
        return false
    }
    return lhs.a == rhs.a
}

But this results in annoying boilerplate for what should be a simple computation, and furthermore fails to solve every case:

let w: A = B(1, 2)
var z: A = B(1, 3)

w == z // still true

I’d be interested to know if there is any interest taken in this problem and whether possible solutions have been discussed. If == were instead behaved as if it were a method of its first argument (as in a .equals(other) method) then the solution above is sufficient to avoid returning the wrong result, but being forced to use .dynamicType for something as basic as equality checking seems cumbersome to me.

FKL


(Chris Lattner) #2

Hi Frederick,

The preferred approach is to allow operators to be defined inside of types. This would allow them to be dynamically dispatched inside of classes.

-Chris

···

On Dec 8, 2015, at 10:32 PM, Frederick Kellison-Linn via swift-evolution <swift-evolution@swift.org> wrote:

Currently, implementing an ‘==‘ function for a custom class can be somewhat cumbersome. In the simple hierarchy:

class A {
    var a: Int
    init(_ a: Int) {
        self.a = a
    }
}

class B : A {
    var b: Int
    init(_ b: Int, _ a: Int) {
        self.b = b
        super.init(a)
    }
}

A reasonable implementation of == would seem to be

func ==(lhs: A, rhs: A) -> Bool {
    return lhs.a == rhs.a
}

func ==(lhs: B, rhs: B) -> Bool {
    return lhs.a == rhs.a &&
           lhs.b == rhs.b
}

I’d be interested to know if there is any interest taken in this problem and whether possible solutions have been discussed. If == were instead behaved as if it were a method of its first argument (as in a .equals(other) method) then the solution above is sufficient to avoid returning the wrong result, but being forced to use .dynamicType for something as basic as equality checking seems cumbersome to me.


(Frederick Kellison-Linn) #3

Hi Chris,

Thanks for the response, I’m glad to hear that. Is this something that is planned/feasible for Swift 3?

FKL

···

On Dec 11, 2015, at 1:02 AM, Chris Lattner <clattner@apple.com> wrote:

On Dec 8, 2015, at 10:32 PM, Frederick Kellison-Linn via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Currently, implementing an ‘==‘ function for a custom class can be somewhat cumbersome. In the simple hierarchy:

class A {
    var a: Int
    init(_ a: Int) {
        self.a = a
    }
}

class B : A {
    var b: Int
    init(_ b: Int, _ a: Int) {
        self.b = b
        super.init(a)
    }
}

A reasonable implementation of == would seem to be

func ==(lhs: A, rhs: A) -> Bool {
    return lhs.a == rhs.a
}

func ==(lhs: B, rhs: B) -> Bool {
    return lhs.a == rhs.a &&
           lhs.b == rhs.b
}

I’d be interested to know if there is any interest taken in this problem and whether possible solutions have been discussed. If == were instead behaved as if it were a method of its first argument (as in a .equals(other) method) then the solution above is sufficient to avoid returning the wrong result, but being forced to use .dynamicType for something as basic as equality checking seems cumbersome to me.

Hi Frederick,

The preferred approach is to allow operators to be defined inside of types. This would allow them to be dynamically dispatched inside of classes.

-Chris


(Chris Lattner) #4

Hi Chris,

Thanks for the response, I’m glad to hear that. Is this something that is planned/feasible for Swift 3?

It’s desired, but not considered critical for Swift 3, so it will depend on how times work out. If ABI stability ends up taking more time than expected, it will probably get pushed out.

-Chris

···

On Dec 11, 2015, at 9:00 AM, Frederick Kellison-Linn <fred.kl@me.com> wrote:

FKL

On Dec 11, 2015, at 1:02 AM, Chris Lattner <clattner@apple.com <mailto:clattner@apple.com>> wrote:

On Dec 8, 2015, at 10:32 PM, Frederick Kellison-Linn via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Currently, implementing an ‘==‘ function for a custom class can be somewhat cumbersome. In the simple hierarchy:

class A {
    var a: Int
    init(_ a: Int) {
        self.a = a
    }
}

class B : A {
    var b: Int
    init(_ b: Int, _ a: Int) {
        self.b = b
        super.init(a)
    }
}

A reasonable implementation of == would seem to be

func ==(lhs: A, rhs: A) -> Bool {
    return lhs.a == rhs.a
}

func ==(lhs: B, rhs: B) -> Bool {
    return lhs.a == rhs.a &&
           lhs.b == rhs.b
}

I’d be interested to know if there is any interest taken in this problem and whether possible solutions have been discussed. If == were instead behaved as if it were a method of its first argument (as in a .equals(other) method) then the solution above is sufficient to avoid returning the wrong result, but being forced to use .dynamicType for something as basic as equality checking seems cumbersome to me.

Hi Frederick,

The preferred approach is to allow operators to be defined inside of types. This would allow them to be dynamically dispatched inside of classes.

-Chris