[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 14:19:56 CDT 2016


> Am 18.07.2016 um 18:23 schrieb Félix Cloutier <felixcca at yahoo.ca>:
> 
> 
>> 1. You cannot say something is unequal although the system says it is equal
> 
> That doesn't make me feel much safer. Comparison returned false when it should have returned true?

Yes you just may have a condition inverted e.g.. You could even have a compiler flag, where you remove the short-circuit and throw an error, if you have a false-negative. This would ease finding such issues (easier than just not having this feature)

> Step into, see why it happened. Sounds like your garden variety of bugs. The downside is that every comparison now starts with an equality comparison of every field, which is not obvious (you can't step into invisible behavior), and does more work than necessary.

There could again be compiler optimizations, that skip this check (in a given mode), but I don’t think in the future this won’t be necessary anymore:
* For small value types the comparison will be very fast.
* For bigger values I hope for such a unique value pool (with copy-on-write) which can be checked with a simple reference comparison (very cheap, very fast).

Invisible behavior is everywhere: Automatic Reference Counting, Existential Containers, Virtual Functions, …

Swift is a high-level language, so it’s main appeal is invisible behavior, but I would call it *rich semantics* or something like that.

>> 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
> 
> I think that a default == for structs that only have Equatable fields makes sense and I can get behind that. We don't need the rest to implement this, though.

The default implementation should definitively not call == on properties to reference types conforming to equatable (by default), since this means that the standard semantics of equality of structs is prone to race conditions. This should be something, that a user can do in a custom implementation, if he really needs to and then he should consider using a synchronous dispatching queue using GCD.

>> 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
> 
> This is hypothetical and in advance of Swift's concurrency model.

It’s proposal. Yes it’s hypothetical. But there are discussions going on at apple already as Arnold Schwaighofer told me on the swift-dev list.

>> 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):
> 
> If I understand the suggested dispatch keyword correctly, it could only work if executables carried metadata for every method,

It is like a virtual method, but for arbitrary parameters, so you need some kind of V-table for that and a lookup, that can be done in constant time. It is something, that I wouldn’t add by default. It is something that you may add to a method explicitly via a keyword (instead of implementing the dynamic dispatch by hand, by calling an `equals:other` method on the left-hand-side and then doing an if-cascade for rhs for example; which is error-prone). So you may use it wisely only, where necessary (and the internal implementation of swift will be much faster, than the manual variant). The XTend language has such a feature.

> which would be a size concern (especially on size-constrained platforms like Apple Watch) and make reverse engineering about as easy as it is in the Objective-C world. We had an improvement here and this would roll it back.

See above. Objective-C has some nice features. I wouldn’t say „roll back to the dynamic world“, but „allow dynamicity where necessary“. We have reference types with virtual functions. This is a consequent step.

> You are imposing the costs not just upon yourself, but upon everybody, on every platform, on every project, forever. Would it be a web app or an operating system component, the only way to opt out is to stop using the default equality and fragment the way that you do comparison. Are you really that confident that this will make everyone happier, now and in the future, than the model that we currently have?

My proposals are just additions, they are not forcing to do anyone anything. They just help to reduce bugs and to have more intuitive (visible) behavior of methods via dynamic dispatch where it is needed.

> Please realize that this is an absolutely radical change. Swift currently has little to no invisible behavior, and currently lets you implement equality any way that you like. In my book, radical changes should be considered with extreme skepticism, and should have an extremely compelling case behind them.

First off, I don’t think, that the changes are that radical and second this example gives a small insight how bad you can implement equality in swift:

>>> 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).
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 496 bytes
Desc: Message signed with OpenPGP using GPGMail
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160718/343c61ca/attachment.sig>


More information about the swift-evolution mailing list