[swift-evolution] Proposals: (1) Forbidding custom `==` for value types, (2) `dispatch` keyword, (3) `default`-result for methods with `Self`, and (4) Poor-Mans-Existentials

Johannes Neubauer neubauer at kingsware.de
Mon Jul 18 04:50:50 CDT 2016


> Am 18.07.2016 um 03:51 schrieb Félix Cloutier <felixcca at yahoo.ca>:
> 
> Your initial rationale no longer makes sense with your suggested solution. If the dumb comparison returns false, people can still introduce side effects in the comparison method, except that now it's even harder to find out because all of my equality tests have been rewritten as "memcmp(a, b) || ==(a, b)“.

No its `memcmp(a, b) && ==(a,b)`, since if the „standard equality“ says `true` there is no short-circuit, but the custom implementation has to be `true` either! It is just a pre-condition.

> What are you trying to protect me from?

1. You cannot say something is unequal although the system says it is equal
2. You do not have to implement equality for value types, only if you really need custom behavior (so you do not write boiler-plate code, which is error prone), so side effects will be less common
3. With unique indirect storage (and copy-on-write) you would be able use `==` for large values, because these values are only shared for reads not for writes  (future, not yet available in swift), so no race conditions
4. With `dispatch` in operator-methods (or any other) as well as a `default` clause for reference types, so that equality of mixed-types just result in `false`, so that this is not possible anymore (see excerpt of discussion):

> Am 16.07.2016 um 15:18 schrieb Johannes Neubauer via swift-evolution <swift-evolution at swift.org>:
> 
> This is not true for reference types. Consider the following **bad** (but compiling code):
> 
> ```swift
> class A: Equatable {}
> 
> class Aa: A {
>    let a: Int
> 
>    init(a: Int) {
>        self.a = a
>    }
> }
> 
> func ==(lhs: A, rhs: A) -> Bool {
>    return lhs === rhs
> }
> 
> func ==(lhs: Aa, rhs: Aa) -> Bool {
>    return lhs.a == rhs.a
> }
> ```
> 
> Now let us use this:
> 
> ```swift
> let a = A()
> let a2 = A()
> let aa = Aa(a: 0)
> let aa2 = Aa(a: 1)
> let aa3 = Aa(a: 1)
> 
> // prints `true`
> print(a == a)
> 
> // prints `false`
> print(a == a2)
> 
> // prints `false`
> print(a == aa)
> 
> // prints `false`
> print(a == aa3)
> 
> // prints `false`
> print(aa == aa2)
> 
> // prints `true` because it compares the `a: Int` values.
> print(aa2 == aa3)
> 
> // now mixed-type comparison (returns `false`)
> print(a == aa2)
> ```
> 
> Hence, you can do mixed-type equality checks in Swift. Even worse is, you can do this:
> 
> ```swift
> let aa2AsA: A = aa2,
>    aa3AsA: A = aa3
> 
> // prints `true` because it compares the `a: Int` values.
> print(aa2 == aa3)
> 
> // prints `false`, because the equals method of `A` is used
> print(aa2AsA == aa3AsA)
> ```
> 
> Just by assigning an object to a variable that is typed differently the result is completely different. This is because method parameters are dispatched statically. This is fast, but results in really unintended results, you can do a **lot** of things breaking the contract of `==` with that. This is why I wanted to add a `default` clause (in *3.* of my original proposal) for such methods involving two references to `Self`. Further on, I wanted to add the keyword `dispatch` for method (and operator) parameters, where dispatching is necessary (see *2.* of my original proposal).




More information about the swift-evolution mailing list