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

Dear Evolutionaries,

First off, I like Swift!

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

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

All the best
Johannes

[0]: swift/GenericsManifesto.md at main · apple/swift · GitHub
[1]: swift/GenericsManifesto.md at main · apple/swift · GitHub

···

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

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 ;) ):

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

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

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’t 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 „equal“ 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’t 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>:

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

Best regards,
Tino

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—two `==` instances should have virtually identical behavior—but 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—most 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

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’t 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 „equal“ 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’t 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]:
Reference vs. Value Types in Swift | Kodeco

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

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’t 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’t 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 „equal“ 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’t 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>:

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

Best regards,
Tino

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

It is only for value types. For reference types, which have an identity, you are right, but it doesn’t 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.

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’t 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’t 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 „large“ 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—two `==` instances should have virtually identical behavior—but 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—most 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:

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’t hold for values.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
-Saagar Jha

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’t hold for values.

Good point. But shouldn’t 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.

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’t 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’t 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 „large“ 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—two `==` instances should have virtually identical behavior—but 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—most 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

And I would do the „standard equality“ upfront even if there is a custom implementation and if the „standard 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’t 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.

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’t 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

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

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’t 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 „standard equality“ upfront even if there is a custom
implementation and if the „standard equality“ says `true`, the custom
implementation is not asked. This would reduce the possibility of
false-negatives.

--
-Saagar Jha

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 „Swift 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’t 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 „standard equality“ upfront even if there is a custom implementation and if the „standard equality“ says `true`, the custom implementation is not asked. This would reduce the possibility of false-negatives.
--
-Saagar Jha

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 „standard equality“ upfront even if there is a custom implementation
and if the „standard equality“ says `true`, the custom implementation is not asked.
This would reduce the possibility of false-negatives.

So, if you’d 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 „standard equality“. If `true` return `true`.
* if „standard 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 „false“-negatives on purpose) add a `transient` keyword or alike. But I wouldn’t 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’t 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>: