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


(Johannes Neubauer) #1

Dear Evolutionaries,

First off, I like Swift!

I have some proposals for swift 3ff. (since the `++` operator is discouraged :wink: ):

1. Custom implementation of equals operator `==` for value types should be forbidden. Rationale: Why has it been added in the first place? For omitting some values from the equals test? A user may introduce side-effects in that very method, i.e., check some global variable. It should to the most be allowed to mark/annotate some properties with `transient` or some better name, in order to prevent it being part of the equals test. But I would discourage the ability to omit some of the values from the equals test, since it does not make sense for value types, does it? If a value instance is equal to another, but the value may differ... this is odd semantics. Properties pointing at reference types should be tested with `===`. The `hashValue` for `Hashable` value types, should be auto-computed, too. Since a value type is "kind of" immutable (will be copied if it is changed), the hash value can be cached.
2. `dispatch` keyword on functions/parameters especially for operators. Which leads to dynamic dispatch of one or more parameters (as defined via the keyword). Rationale: Swift is a multi-paradigm language. It is neither as hard with immutability as Haskell is, but not as open as Python or whatever. I see Swift as a golden middle course. This is why in Swift value and reference types coexist, isn't it? So, although the focus seems to lie on value types and static dispatch, the dynamic world still is your frient, right? Adding dynamic/multi dispatch on-demand would be great. Now I have to implement it by defining an operator on a super type and implement an instance method, which the operator can delegate to. Last but not least, in the instance method I have to do the dispatch of the second parameter by hand. This feature could be used wonderfully together with value types using your great Existential Containers, too.
3. As a follow-up to *2.*: `default`-case for methods/operators like equals (`==`) to allow safe polymorphic calls for mixed types (or even complete pattern matching for method overloading, like many functional languages do it for having functions with different entry points for different values). Rationale: If you could add a `default`-case (e.g. as an additional statement to the `return` or beneath the definition of the return type in the method head) which returns `false`, then you could safely implement all `T == T` checks and all `T == U` checks will just return false. This does not make sense for all methods, but I can imagine of more than just equals :wink: . This way swift could soften the requirement of PATs (Protocol with Associated Types) with the associated type `Self`. This would be a very first step to existentials... leading to the next and last proposal for today
4. Add poor-mans existentials which allow to access non-PAT methods as well as methods with a `default`-case (see *3.*). Rationale: I read the [generics manifesto][0] and really liked the part [existentials][1]. In a first step I wouldn't do complicated things like chaining existentials, but do something similar like in structs with `mutating` functions: You just cannot call them on `let` variables (compiler error). The same could be done for methods using the associated type in their signature (because of the co-/contravariance problem) unless the associated type is `Self` and the function has a `default`-clause. By the way I think, that not implementing pseudo-existentials (like Java and Kotlin do with their wildcards or variance declarations `in`/`out` respectively) and I hope that the existentials will come to swift in the future... I was a little bit afraid that the "Existentials" section is beneath "Unlikely" and "Potential Removals", but since these headings are on the same level, this does not mean that implementing existentials in the future are unlikely, does it? I have some ideas how they could fit into the language very well, if you like to hear them...?

I would really appreciate discussing these 4 proposals :slight_smile: .

All the best
Johannes

[0]: https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md
[1]: https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#existentials

路路路

--
Dr. Johannes Neubauer
E-Mail: neubauer@kingsware.de
WWW : http://www.kingsware.de


(Daniel Resnick) #2

Forbidding custom `==` for value types would break equality for all Swift
collection types, since they use reference types behind the scenes for
storing memory and copy on write semantics. Furthermore, why should
properties pointing to reference types always use `===`? What if the
reference type conforms to `Equatable`?

路路路

On Thu, Jul 14, 2016 at 1:36 PM, Johannes Neubauer via swift-evolution < swift-evolution@swift.org> wrote:

Dear Evolutionaries,

First off, I like Swift!

I have some proposals for swift 3ff. (since the `++` operator is
discouraged :wink: ):

1. Custom implementation of equals operator `==` for value types should be
forbidden. Rationale: Why has it been added in the first place? For
omitting some values from the equals test? A user may introduce
side-effects in that very method, i.e., check some global variable. It
should to the most be allowed to mark/annotate some properties with
`transient` or some better name, in order to prevent it being part of the
equals test. But I would discourage the ability to omit some of the values
from the equals test, since it does not make sense for value types, does
it? If a value instance is equal to another, but the value may differ...
this is odd semantics. Properties pointing at reference types should be
tested with `===`. The `hashValue` for `Hashable` value types, should be
auto-computed, too. Since a value type is "kind of" immutable (will be
copied if it is changed), the hash value can be cached.
2. `dispatch` keyword on functions/parameters especially for operators.
Which leads to dynamic dispatch of one or more parameters (as defined via
the keyword). Rationale: Swift is a multi-paradigm language. It is neither
as hard with immutability as Haskell is, but not as open as Python or
whatever. I see Swift as a golden middle course. This is why in Swift value
and reference types coexist, isn't it? So, although the focus seems to lie
on value types and static dispatch, the dynamic world still is your frient,
right? Adding dynamic/multi dispatch on-demand would be great. Now I have
to implement it by defining an operator on a super type and implement an
instance method, which the operator can delegate to. Last but not least, in
the instance method I have to do the dispatch of the second parameter by
hand. This feature could be used wonderfully together with value types
using your great Existential Containers, too.
3. As a follow-up to *2.*: `default`-case for methods/operators like
equals (`==`) to allow safe polymorphic calls for mixed types (or even
complete pattern matching for method overloading, like many functional
languages do it for having functions with different entry points for
different values). Rationale: If you could add a `default`-case (e.g. as an
additional statement to the `return` or beneath the definition of the
return type in the method head) which returns `false`, then you could
safely implement all `T == T` checks and all `T == U` checks will just
return false. This does not make sense for all methods, but I can imagine
of more than just equals :wink: . This way swift could soften the requirement
of PATs (Protocol with Associated Types) with the associated type `Self`.
This would be a very first step to existentials... leading to the next and
last proposal for today
4. Add poor-mans existentials which allow to access non-PAT methods as
well as methods with a `default`-case (see *3.*). Rationale: I read the
[generics manifesto][0] and really liked the part [existentials][1]. In a
first step I wouldn't do complicated things like chaining existentials, but
do something similar like in structs with `mutating` functions: You just
cannot call them on `let` variables (compiler error). The same could be
done for methods using the associated type in their signature (because of
the co-/contravariance problem) unless the associated type is `Self` and
the function has a `default`-clause. By the way I think, that not
implementing pseudo-existentials (like Java and Kotlin do with their
wildcards or variance declarations `in`/`out` respectively) and I hope that
the existentials will come to swift in the future... I was a little bit
afraid that the "Existentials" section is beneath "Unlikely" and "Potential
Removals", but since these headings are on the same level, this does not
mean that implementing existentials in the future are unlikely, does it? I
have some ideas how they could fit into the language very well, if you like
to hear them...?

I would really appreciate discussing these 4 proposals :slight_smile: .

All the best
Johannes

[0]: https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md
[1]:
https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#existentials

--
Dr. Johannes Neubauer
E-Mail: neubauer@kingsware.de
WWW : http://www.kingsware.de

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


(Johannes Neubauer) #3

Hi Daniel,

I will answer backwards, since it is easier for me this way.

Furthermore, why should properties pointing to reference types always use `===`? What if the reference type conforms to `Equatable`?

I proposed to use `===`, since a value of a reference a value type is referencing is the reference itself. But point taken. So a solution could be to use `==` for reference types implementing `Equatable`. But wait for the punch-line below ;).

Forbidding custom `==` for value types would break equality for all Swift collection types, since they use reference types behind the scenes for storing memory and copy on write semantics.

If the storage implements `Equatable`, this problem should be solved, right?

Anyway (punch-line following), although I don鈥檛 know how exactly the low-level Value-Witness-Table works, but if it works as I would expect, then there should be only exactly one entry per 鈥瀍qual鈥 storage, and all collection types with the same data point to the same reference (which is very memory efficient and table lookup using hashing should be constant time). If this is correct, a check for `===` suffices. AFAIK, the low-level swift implementation already checks for `===` on value types that are stored on the heap (see [this blog post][0]) and don鈥檛 bother calling `==` if `===` holds.

All the best
Johannes

[0]: https://www.raywenderlich.com/112029/reference-value-types-in-swift-part-2

路路路

Am 14.07.2016 um 22:47 schrieb Daniel Resnick <danielzresnick@gmail.com>:


(Tino) #4

Hello Johannes,

路路路

Am 14.07.2016 um 22:36 schrieb Johannes Neubauer via swift-evolution <swift-evolution@swift.org>:

1. Custom implementation of equals operator `==` for value types should be forbidden. Rationale: Why has it been added in the first place? For omitting some values from the equals test?

It would be nice to have an easy way to conform to Hashable & Equatable without writing much boilerplate (it's possible in other existing languages), but I see no motivation to forbid custom implementations 鈥 not only because you are free to stay away from those, but also because there can be good reasons to do so:
Equality isn't always as simple as it seems, and many developers have been bitten by issues with floating point types鈥 instead of "a == b", often "abs(a - b) < delta" is the right choice, and a custom "==" is the only option to specify delta (given a situation where you are comparing float-members of a struct).

For the other three points, I'd suggest to present each of them later in separate threads 鈥 it's already hard to follow each topic, and it would be even harder when there are three different proposals in a single thread :wink:

Best regards,
Tino


(Brent Royal-Gordon) #5

1. Custom implementation of equals operator `==` for value types should be forbidden. Rationale: Why has it been added in the first place? For omitting some values from the equals test?

This limitation would prevent you from even implementing IEEE floating-point semantics (+0.0 == -0.0, while NaN != NaN; neither would be allowed by this rule). It would similarly prevent you from implementing even moderately

Properties pointing at reference types should be tested with `===`.

This rule in particular would also prevent you from implementing Equatable semantics for most of the copy-on-write containers (String, Array, Set, Dictionary), which all use an internal reference to a memory buffer. And several of Foundation's struct wrappers, like the `Data` type that wraps `NSData`. And any value type of your own which contains a lot of internal data and which, after a lot of testing and benchmarking, you determine would be more efficiently implemented as a value type wrapping a reference type with copy-on-write semantics. And who knows what else.

Ultimately, it boils down to this:

If a value instance is equal to another, but the value may differ... this is odd semantics.

Equality is how we define whether two instances differ: If `==` returns `true`, then they don't differ. Now, there are some important commonsense rules about this鈥攖wo `==` instances should have virtually identical behavior鈥攂ut you can't just ignore the widespread need for this feature. If Swift didn't offer `Equatable` overloading, we would have to invent it ourselves鈥攎ost likely with some sort of half-baked workaround that would cause confusion about whether you ought to use "pure" equality or "smart" equality in any given situation. Think of the `==` vs. `===` mess in JavaScript and you'll get an idea of what I mean.

路路路

On Jul 14, 2016, at 1:36 PM, Johannes Neubauer via swift-evolution <swift-evolution@swift.org> wrote:

--
Brent Royal-Gordon
Architechies


(Austin Zheng) #6

Additive proposals are out of scope for Swift 3; you'll have better luck if
you come back around August and propose these one at a time.

I don't see the need for "poor man's existentials" - existential
improvements are out of scope for the next release, and there are already a
bunch of designs floating around. You might want to search the archives for
more details. #3 falls out of a proper implementation of existential types
that support protocols with associated types and self requirements.

If the storage implements `Equatable`, this problem should be solved,
right?

Anyway (punch-line following), although I don鈥檛 know how exactly the
low-level Value-Witness-Table works, but if it works as I would expect,
then there should be only exactly one entry per 鈥瀍qual鈥 storage, and all
collection types with the same data point to the same reference (which is
very memory efficient and table lookup using hashing should be constant
time). If this is correct, a check for `===` suffices. AFAIK, the low-level
swift implementation already checks for `===` on value types that are
stored on the heap (see [this blog post][0]) and don鈥檛 bother calling `==`
if `===` holds.

This isn't true. Two buffers can have the same contents and therefore be
equal, but be distinct from each other in terms of identity.

路路路

All the best
Johannes

[0]:
https://www.raywenderlich.com/112029/reference-value-types-in-swift-part-2

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


(Johannes Neubauer) #7

Hi Austin,

Additive proposals are out of scope for Swift 3; you'll have better luck if you come back around August and propose these one at a time.

Ok. I didn鈥檛 know that proposals can be only for the next release. I will come back later ;).

I don't see the need for "poor man's existentials" - existential improvements are out of scope for the next release, and there are already a bunch of designs floating around. You might want to search the archives for more details. #3 falls out of a proper implementation of existential types that support protocols with associated types and self requirements.

If the storage implements `Equatable`, this problem should be solved, right?

Anyway (punch-line following), although I don鈥檛 know how exactly the low-level Value-Witness-Table works, but if it works as I would expect, then there should be only exactly one entry per 鈥瀍qual鈥 storage, and all collection types with the same data point to the same reference (which is very memory efficient and table lookup using hashing should be constant time). If this is correct, a check for `===` suffices. AFAIK, the low-level swift implementation already checks for `===` on value types that are stored on the heap (see [this blog post][0]) and don鈥檛 bother calling `==` if `===` holds.

This isn't true. Two buffers can have the same contents and therefore be equal, but be distinct from each other in terms of identity.

Yeah, I had a discussion on swift-dev about this, too. There were some misleading blog posts out there. But perhaps it should be that way鈥 could be another proposal, but not for Swift 3.

[0]: https://www.raywenderlich.com/112029/reference-value-types-in-swift-part-2

All the best
Johannes

路路路

Am 15.07.2016 um 00:35 schrieb Austin Zheng <austinzheng@gmail.com>:


(Shawn Erickson) #8

Additional two "things" maybe considered equal in the chosen problem domain
despite their identity (memory location, etc.) being different. Having the
ability to supply custom hash and equality for types can be a useful tool
in a developers toolbox. For example two pathways of code may create what
is actually the same thing (say a reference to the same file on disk) then
want to work with stdlib set and/or dictionary with the equivalent things
being resolved correctly, etc.

To remove custom equality will limit some designs and to force identity
based equality with present similar problems on the other end of the
spectrum.

-Shawn

路路路

On Fri, Jul 15, 2016 at 8:37 AM Tino Heth via swift-evolution < swift-evolution@swift.org> wrote:

Hello Johannes,

> Am 14.07.2016 um 22:36 schrieb Johannes Neubauer via swift-evolution < > swift-evolution@swift.org>:
>
> 1. Custom implementation of equals operator `==` for value types should
be forbidden. Rationale: Why has it been added in the first place? For
omitting some values from the equals test?

It would be nice to have an easy way to conform to Hashable & Equatable
without writing much boilerplate (it's possible in other existing
languages), but I see no motivation to forbid custom implementations 鈥 not
only because you are free to stay away from those, but also because there
can be good reasons to do so:
Equality isn't always as simple as it seems, and many developers have been
bitten by issues with floating point types鈥 instead of "a == b", often
"abs(a - b) < delta" is the right choice, and a custom "==" is the only
option to specify delta (given a situation where you are comparing
float-members of a struct).

For the other three points, I'd suggest to present each of them later in
separate threads 鈥 it's already hard to follow each topic, and it would be
even harder when there are three different proposals in a single thread :wink:

Best regards,
Tino

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


(Johannes Neubauer) #9

It is only for value types. For reference types, which have an identity, you are right, but it doesn鈥檛 hold for values.

路路路

Am 15.07.2016 um 15:19 schrieb Shawn Erickson <shawnce@gmail.com>:

Additional two "things" maybe considered equal in the chosen problem domain despite their identity (memory location, etc.) being different. Having the ability to supply custom hash and equality for types can be a useful tool in a developers toolbox. For example two pathways of code may create what is actually the same thing (say a reference to the same file on disk) then want to work with stdlib set and/or dictionary with the equivalent things being resolved correctly, etc.

To remove custom equality will limit some designs and to force identity based equality with present similar problems on the other end of the spectrum.


(Johannes Neubauer) #10

1. Custom implementation of equals operator `==` for value types should be forbidden. Rationale: Why has it been added in the first place? For omitting some values from the equals test?

This limitation would prevent you from even implementing IEEE floating-point semantics (+0.0 == -0.0, while NaN != NaN; neither would be allowed by this rule). It would similarly prevent you from implementing even moderately

Didn鈥檛 you follow the correspondence of these proposals? There are corner cases (like this one above), but there should be a default equality check and you shouldn鈥檛 be able to decide, that something is not equal, which the default equality check says that it is equal. In addition, you mention basic types of the swift language. Of course their have to be some axioms in the language which have to be created by the language designers. Floating-point semantics fall exactly into this category.

Properties pointing at reference types should be tested with `===`.

This rule in particular would also prevent you from implementing Equatable semantics for most of the copy-on-write containers (String, Array, Set, Dictionary), which all use an internal reference to a memory buffer. And several of Foundation's struct wrappers, like the `Data` type that wraps `NSData`. And any value type of your own which contains a lot of internal data and which, after a lot of testing and benchmarking, you determine would be more efficiently implemented as a value type wrapping a reference type with copy-on-write semantics. And who knows what else.

Please, you should really follow the discussion here. I already lined out all these cases. I wrote this before Arnold from apple told me that some blog posts out there are wrong about how value types work internally. This is something that can be done in the future, as soon as swift has something like automatic indirect storage with a unique value pool and with copy-on-write semantics for 鈥瀕arge鈥 value types.

Ultimately, it boils down to this:

If a value instance is equal to another, but the value may differ... this is odd semantics.

Equality is how we define whether two instances differ: If `==` returns `true`, then they don't differ. Now, there are some important commonsense rules about this鈥攖wo `==` instances should have virtually identical behavior鈥攂ut you can't just ignore the widespread need for this feature. If Swift didn't offer `Equatable` overloading, we would have to invent it ourselves鈥攎ost likely with some sort of half-baked workaround that would cause confusion about whether you ought to use "pure" equality or "smart" equality in any given situation. Think of the `==` vs. `===` mess in JavaScript and you'll get an idea of what I mean.

Really, please read especially my last mails. My proposal does not prevent you from doing this differentiation directly in the language, but prevents you from doing some nasty bugs.

路路路

Am 17.07.2016 um 15:11 schrieb Brent Royal-Gordon <brent@architechies.com>:

On Jul 14, 2016, at 1:36 PM, Johannes Neubauer via swift-evolution <swift-evolution@swift.org> wrote:


(Saagar Jha) #11

Here's a value type that uses custom equality (at least, I think so):
String. Since it uses extended grapheme clusters, internally two Strings
may be composed of different Unicode scalars, but if they create the same
Characters they are considered to be equal.

路路路

On Fri, Jul 15, 2016 at 09:12 Johannes Neubauer via swift-evolution < swift-evolution@swift.org> wrote:

> Am 15.07.2016 um 15:19 schrieb Shawn Erickson <shawnce@gmail.com>:
>
> Additional two "things" maybe considered equal in the chosen problem
domain despite their identity (memory location, etc.) being different.
Having the ability to supply custom hash and equality for types can be a
useful tool in a developers toolbox. For example two pathways of code may
create what is actually the same thing (say a reference to the same file on
disk) then want to work with stdlib set and/or dictionary with the
equivalent things being resolved correctly, etc.
>
> To remove custom equality will limit some designs and to force identity
based equality with present similar problems on the other end of the
spectrum.

It is only for value types. For reference types, which have an identity,
you are right, but it doesn鈥檛 hold for values.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
-Saagar Jha


(Johannes Neubauer) #12

Sorry, this can be misunderstood. I meant: I would remove custom equality only for value types (for the reasons I meant above). The fixed implementation of checking equality for value types would check equality `==` of all referenced structs and reference types implementing `Equatable`. If a reference type does not implement `Equatable` `===` will be used.

If the shiny days will come, where indirect storage is used for value types with at least two properties with reference types (or other value types with indirect storage) **automatically**, with the guarantee that these storages are uniquely stored in a given value table/pool, then `===` can be used for that too (e.g. for the storage of Arrays or Strings, which would then be automatically generated either!).

路路路

Am 15.07.2016 um 18:12 schrieb Johannes Neubauer via swift-evolution <swift-evolution@swift.org>:

Am 15.07.2016 um 15:19 schrieb Shawn Erickson <shawnce@gmail.com>:

Additional two "things" maybe considered equal in the chosen problem domain despite their identity (memory location, etc.) being different. Having the ability to supply custom hash and equality for types can be a useful tool in a developers toolbox. For example two pathways of code may create what is actually the same thing (say a reference to the same file on disk) then want to work with stdlib set and/or dictionary with the equivalent things being resolved correctly, etc.

To remove custom equality will limit some designs and to force identity based equality with present similar problems on the other end of the spectrum.

It is only for value types. For reference types, which have an identity, you are right, but it doesn鈥檛 hold for values.


(Johannes Neubauer) #13

Good point. But shouldn鈥檛 this be another type of equality then or do they behave exactly the same and are just implemented differently? Because if not, this seems to be introducing mixed-type comparisons like `5l == 5` or `Point2D(0, 0) == Point3D(0, 0, 0) which are bad since they make it impossible to guarantee reflexivity, symmetry, and transitivity. Swift does a hard job to enforce this from the programmer. If this is really intended, then there should be a fixed implementation for equality of value types, which can be overridden, but which leads to a warning (which has to be suppressed with some kind of annotation or so). Because, these custom implementations can do harm.

路路路

Am 15.07.2016 um 18:29 schrieb Saagar Jha <saagarjha28@gmail.com>:

Here's a value type that uses custom equality (at least, I think so): String. Since it uses extended grapheme clusters, internally two Strings may be composed of different Unicode scalars, but if they create the same Characters they are considered to be equal.


(F茅lix Cloutier) #14

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)".

What are you trying to protect me from?

F茅lix

路路路

Le 17 juil. 2016 脿 06:30:42, Johannes Neubauer via swift-evolution <swift-evolution@swift.org> a 茅crit :

Am 17.07.2016 um 15:11 schrieb Brent Royal-Gordon <brent@architechies.com>:

On Jul 14, 2016, at 1:36 PM, Johannes Neubauer via swift-evolution <swift-evolution@swift.org> wrote:

1. Custom implementation of equals operator `==` for value types should be forbidden. Rationale: Why has it been added in the first place? For omitting some values from the equals test?

This limitation would prevent you from even implementing IEEE floating-point semantics (+0.0 == -0.0, while NaN != NaN; neither would be allowed by this rule). It would similarly prevent you from implementing even moderately

Didn鈥檛 you follow the correspondence of these proposals? There are corner cases (like this one above), but there should be a default equality check and you shouldn鈥檛 be able to decide, that something is not equal, which the default equality check says that it is equal. In addition, you mention basic types of the swift language. Of course their have to be some axioms in the language which have to be created by the language designers. Floating-point semantics fall exactly into this category.

Properties pointing at reference types should be tested with `===`.

This rule in particular would also prevent you from implementing Equatable semantics for most of the copy-on-write containers (String, Array, Set, Dictionary), which all use an internal reference to a memory buffer. And several of Foundation's struct wrappers, like the `Data` type that wraps `NSData`. And any value type of your own which contains a lot of internal data and which, after a lot of testing and benchmarking, you determine would be more efficiently implemented as a value type wrapping a reference type with copy-on-write semantics. And who knows what else.

Please, you should really follow the discussion here. I already lined out all these cases. I wrote this before Arnold from apple told me that some blog posts out there are wrong about how value types work internally. This is something that can be done in the future, as soon as swift has something like automatic indirect storage with a unique value pool and with copy-on-write semantics for 鈥瀕arge鈥 value types.

Ultimately, it boils down to this:

If a value instance is equal to another, but the value may differ... this is odd semantics.

Equality is how we define whether two instances differ: If `==` returns `true`, then they don't differ. Now, there are some important commonsense rules about this鈥攖wo `==` instances should have virtually identical behavior鈥攂ut you can't just ignore the widespread need for this feature. If Swift didn't offer `Equatable` overloading, we would have to invent it ourselves鈥攎ost likely with some sort of half-baked workaround that would cause confusion about whether you ought to use "pure" equality or "smart" equality in any given situation. Think of the `==` vs. `===` mess in JavaScript and you'll get an idea of what I mean.

Really, please read especially my last mails. My proposal does not prevent you from doing this differentiation directly in the language, but prevents you from doing some nasty bugs.

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


(Johannes Neubauer) #15

And I would do the 鈥瀞tandard equality鈥 upfront even if there is a custom implementation and if the 鈥瀞tandard equality鈥 says `true`, the custom implementation is not asked. This would reduce the possibility of false-negatives.

路路路

Am 15.07.2016 um 18:41 schrieb Johannes Neubauer via swift-evolution <swift-evolution@swift.org>:

Am 15.07.2016 um 18:29 schrieb Saagar Jha <saagarjha28@gmail.com>:

Here's a value type that uses custom equality (at least, I think so): String. Since it uses extended grapheme clusters, internally two Strings may be composed of different Unicode scalars, but if they create the same Characters they are considered to be equal.

Good point. But shouldn鈥檛 this be another type of equality then or do they behave exactly the same and are just implemented differently? Because if not, this seems to be introducing mixed-type comparisons like `5l == 5` or `Point2D(0, 0) == Point3D(0, 0, 0) which are bad since they make it impossible to guarantee reflexivity, symmetry, and transitivity. Swift does a hard job to enforce this from the programmer. If this is really intended, then there should be a fixed implementation for equality of value types, which can be overridden, but which leads to a warning (which has to be suppressed with some kind of annotation or so). Because, these custom implementations can do harm.


#16

How about Polar(r: 0, phi: 0) ?
It should all equal with any angles if r == 0.

路路路

2016-07-16 0:41 GMT+08:00 Johannes Neubauer via swift-evolution < swift-evolution@swift.org>:

> Am 15.07.2016 um 18:29 schrieb Saagar Jha <saagarjha28@gmail.com>:
>
> Here's a value type that uses custom equality (at least, I think so):
String. Since it uses extended grapheme clusters, internally two Strings
may be composed of different Unicode scalars, but if they create the same
Characters they are considered to be equal.

Good point. But shouldn鈥檛 this be another type of equality then or do they
behave exactly the same and are just implemented differently? Because if
not, this seems to be introducing mixed-type comparisons like `5l == 5` or
`Point2D(0, 0) == Point3D(0, 0, 0) which are bad since they make it
impossible to guarantee reflexivity, symmetry, and transitivity. Swift does
a hard job to enforce this from the programmer. If this is really intended,
then there should be a fixed implementation for equality of value types,
which can be overridden, but which leads to a warning (which has to be
suppressed with some kind of annotation or so). Because, these custom
implementations can do harm.

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


(Johannes Neubauer) #17

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 鈥瀞tandard 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 18.07.2016 um 03:51 schrieb F茅lix Cloutier <felixcca@yahoo.ca>:

Am 16.07.2016 um 15:18 schrieb Johannes Neubauer via swift-evolution <swift-evolution@swift.org>:

This is not true for reference types. Consider the following **bad** (but compiling code):

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:

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:

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).


(Saagar Jha) #18

Equatable, where the == operator is defined, will not let you compare two
things of a different type.

路路路

On Fri, Jul 15, 2016 at 10:02 Johannes Neubauer <neubauer@kingsware.de> wrote:

> Am 15.07.2016 um 18:41 schrieb Johannes Neubauer via swift-evolution < > swift-evolution@swift.org>:
>
>
>> Am 15.07.2016 um 18:29 schrieb Saagar Jha <saagarjha28@gmail.com>:
>>
>> Here's a value type that uses custom equality (at least, I think so):
String. Since it uses extended grapheme clusters, internally two Strings
may be composed of different Unicode scalars, but if they create the same
Characters they are considered to be equal.
>
> Good point. But shouldn鈥檛 this be another type of equality then or do
they behave exactly the same and are just implemented differently? Because
if not, this seems to be introducing mixed-type comparisons like `5l == 5`
or `Point2D(0, 0) == Point3D(0, 0, 0) which are bad since they make it
impossible to guarantee reflexivity, symmetry, and transitivity. Swift does
a hard job to enforce this from the programmer. If this is really intended,
then there should be a fixed implementation for equality of value types,
which can be overridden, but which leads to a warning (which has to be
suppressed with some kind of annotation or so). Because, these custom
implementations can do harm.

And I would do the 鈥瀞tandard equality鈥 upfront even if there is a custom
implementation and if the 鈥瀞tandard equality鈥 says `true`, the custom
implementation is not asked. This would reduce the possibility of
false-negatives.

--
-Saagar Jha


(Johannes Neubauer) #19

Equatable, where the == operator is defined, will not let you compare two things of a different type.

I know that. I mean, this is what I meant with 鈥濻wift does a hard job to enforce this鈥.

路路路

Am 15.07.2016 um 19:05 schrieb Saagar Jha <saagarjha28@gmail.com>:

On Fri, Jul 15, 2016 at 10:02 Johannes Neubauer <neubauer@kingsware.de> wrote:

> Am 15.07.2016 um 18:41 schrieb Johannes Neubauer via swift-evolution <swift-evolution@swift.org>:
>
>
>> Am 15.07.2016 um 18:29 schrieb Saagar Jha <saagarjha28@gmail.com>:
>>
>> Here's a value type that uses custom equality (at least, I think so): String. Since it uses extended grapheme clusters, internally two Strings may be composed of different Unicode scalars, but if they create the same Characters they are considered to be equal.
>
> Good point. But shouldn鈥檛 this be another type of equality then or do they behave exactly the same and are just implemented differently? Because if not, this seems to be introducing mixed-type comparisons like `5l == 5` or `Point2D(0, 0) == Point3D(0, 0, 0) which are bad since they make it impossible to guarantee reflexivity, symmetry, and transitivity. Swift does a hard job to enforce this from the programmer. If this is really intended, then there should be a fixed implementation for equality of value types, which can be overridden, but which leads to a warning (which has to be suppressed with some kind of annotation or so). Because, these custom implementations can do harm.

And I would do the 鈥瀞tandard equality鈥 upfront even if there is a custom implementation and if the 鈥瀞tandard equality鈥 says `true`, the custom implementation is not asked. This would reduce the possibility of false-negatives.
--
-Saagar Jha


(Johannes Neubauer) #20

How about Polar(r: 0, phi: 0) ?
It should all equal with any angles if r == 0.

In an earlier post I wrote:

And I would do the 鈥瀞tandard equality鈥 upfront even if there is a custom implementation
and if the 鈥瀞tandard equality鈥 says `true`, the custom implementation is not asked.
This would reduce the possibility of false-negatives.

So, if you鈥檇 like to override the "standard equality鈥 you would be able to do this, but with this chain you cannot create false-negatives as easy:

* for heap allocated values (e.g. values bigger than the value buffer in an existential container for protocol types) do a `===`. If `true` return `true`.
* for stack allocated values use 鈥瀞tandard equality鈥. If `true` return `true`.
* if 鈥瀞tandard equality鈥 return `false`, execute possible custom equality check `==`. Return result.

But If you override `==` in a value type I would warn, because this makes sense in only very rare occasions (perhaps with a @suppress annotation or alike). If you would like to ignore some values (produce 鈥瀎alse鈥-negatives on purpose) add a `transient` keyword or alike. But I wouldn鈥檛 allow the latter, but instead offer the developer best practices like value types with indirect storage and copy-on-write for value semantics, in order to omit the values in the `==` implementation of the indirect storage reference type. Last but not least, in a far future, when the indirect storage of value types is done automatically (potentially) and these values are uniquely stored on the heap (in some kind of value pool), you can check for them `===`, too. The latter would **not** be as easy, if we allow custom equality because if we store values uniquely in the value pool and the equality puts values in the same equivalence class that are actually **not** equal from a value types point of view (be it for false-positives and false-negatives), it would put the values into the same slot, since you don鈥檛 have an identity check (`===`) on values.

In summary, IMHO using custom equality for value types is dangerous as it puts values that are actually different into the same equivalence class, which make their usage in dictionaries very difficult (especially for false-negatives, which hold some context information not part of the value) and correspondingly the implementation of value pools hard. So, custom equality should be used only in exceptional cases and it should be possible to do at most false-positives (like in the `Polar(r:0, phi: whatever)` example), but for false-negatives (like in the URI example) it should be handled differently, by introducing indirection.

路路路

Am 16.07.2016 um 03:53 schrieb Susan Cheng <susan.doggie@gmail.com>: