Universal Equatability, Hashability, and Comparability

or a desired one.

That's true. But what's important is that it's consistent with == and
allows lookup in a sorted container. The core ordering operation should
not be spelled "<" though; we should use "<=>" for that so that types
can keep their domain-specific "<" if necessary. Too bad the same trick
doesn't work for "==" :-).

Many people have complained that 'nil < .Some(1)' works for optionals,
for instance, ordering 'nil' below Some values. We could use pointer
identity to order class instances and types, but this wouldn't be a
stable ordering across process runs.

Oh, that's what you mean by "stable." So what?

What if we separated "ordering for a collection" from the < == > family altogether? Even Floats can be given a true equivalence relationship and assigned a stable total ordering for the purposes of container identity. I think it's reasonable for types to be able to provide an abstract ordering without making '<' and friends casually work. Programmers may have had "don't rely on hash order" drilled into their heads over the decades, and "don't rely on container order" might be a reasonable abstraction step from that, but "don't rely on < behavior" strikes me as unintuitive and going against the intuition users build up from common concrete manifestations of '<', such as numeric types.

+1 to this. There are many advantages to this approach and I am surprised that no languages (that I know of) have done this.

···

Sent from my iPhone

On Mar 9, 2016, at 12:51 PM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On Mar 9, 2016, at 11:41 AM, Dave Abrahams <dabrahams@apple.com> wrote:

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

I think we might (at least partially) be in violent agreement :). Most (if
not everyone) on this thread has agreed that painless opt-in
auto-conformance is a good thing ("struct Foo : Regular { .. }"), albeit
with differing definitions of 'painless'. But I maintain that having a
"func ==(lhs: Any, rhs: Any) -> Bool" stdlib fallback implementation of ==
is a lot of potential pain for very little benefit.

Austin

···

On Wed, Mar 9, 2016 at 1:17 PM, Dave Abrahams via swift-evolution < swift-evolution@swift.org> wrote:

on Tue Mar 08 2016, Brent Royal-Gordon <brent-AT-architechies.com> wrote:

>> - Function types in Swift do not provide a ready equality
> operation. We could provide a default implementation that always
> returns 'false', perhaps.
>
> I think this sort of puts the lie to the idea.
>
> We can always provide *a* definition of equality, but I suspect it
> will often be an *incorrect* definition.

I disagree; IMO it would seldom be incorrect.
But I wouldn't necessarily want to make everything equatable. I'd want
to give many things an equatable conformance that's available simply by
declaring it. In fact, I'd like to be able to say:

          struct Something : Regular {
              // Stored properties that are all Regular
          }

and get Equatable, Comparable, and Hashable for free.

> That's why you had to suggest functions should always be false: you
> cannot (without more effort than you want to spend) provide a correct
> definition of equality for it.
>
> I mean, imagine what happens if you make functions Equatable and
> Hashable but with definitions that don't actually work. Currently,
> `Set<Void -> Void>` gives you an error:
>
> error: type 'Void -> Void' does not conform to protocol 'Hashable'
>
> But with this feature in place, Swift would happily produce a Set of
> functions which collides endlessly, doesn't do any uniquing, never
> says it contains any value you pass into it, and can only remove
> elements by index.
>
> A type that is "never equal" completely breaks Set in practice, and
> there's no way for the type system to catch the problem.
>
> If we automatically synthesize a == operator for every type, many of
> those operators will be incorrect. For instance, anything that
> includes a cache will be incorrect.
> Anything that includes a pointer to a buffer and ought to evaluate the
> buffer's contents will be incorrect.
> Anything that includes a closure will be incorrect. Individually, each
> of these cases is minor, but they multiply and interact with each
> other until, together, they undermine confidence in ==.

These things wouldn't be Regular by themselves. To make them
composable, you can create a single Regular value type that wraps each
one.

> If you explicitly mark things as Equatable, then it is clear that the
> equality operator on them really does have a sensible definition. But
> if you can pass anything into ==, you will never know what will
> actually *work*. If everything's Equatable, then nothing is.
>
> * * *
>
> Auto-deriving is a different story, though, especially if it's opt-in
> (you have to say `deriving Equatable`). There, you presumably have
> looked at the default semantics and determined they're appropriate for
> your type.

I don't know if that level of explicitness is needed. Explicit
declaration of conformance might be enough.

> But I think it's clear that derived conformances should eventually be
> a user-accessible feature. We already derive RawRepresentable and
> ErrorType on enums. (We also derive initializers on some types, but
> that's arguably a separate feature.) I think it's clear that Swift ∞
> ought to allow you to derive protocol conformances; it's just a matter
> of scheduling.
>
> So I think that what we ought to do is this:
>
> • Make a best guess at what Swift ∞ would want you to do to invoke the
> user-specified derivation logic for an arbitrary protocol—implicit
> derivation or something marked by a keyword.
> • Think about whether derived conformances of Equatable, Hashable,
> and/or Comparable are urgent enough that we should implement them
> before the general feature.
> • If so, design these features along the lines of what we would expect
> the eventual user-specified derivation feature to use.

Of course.

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

or a desired one.

That's true. But what's important is that it's consistent with == and

allows lookup in a sorted container. The core ordering operation should
not be spelled "<" though; we should use "<=>" for that so that types
can keep their domain-specific "<" if necessary. Too bad the same trick
doesn't work for "==" :-).

Many people have complained that 'nil < .Some(1)' works for optionals,
for instance, ordering 'nil' below Some values. We could use pointer
identity to order class instances and types, but this wouldn't be a
stable ordering across process runs.

Oh, that's what you mean by "stable." So what?

What if we separated "ordering for a collection" from the < == >
family altogether?

Yes, that's what I was suggesting we do with <=>. Heh, that works for
== too, I hadn't realized!

Even Floats can be given a true equivalence relationship and assigned
a stable total ordering for the purposes of container identity. I
think it's reasonable for types to be able to provide an abstract
ordering without making '<' and friends casually work. Programmers may
have had "don't rely on hash order" drilled into their heads over the
decades, and "don't rely on container order" might be a reasonable
abstraction step from that, but "don't rely on < behavior" strikes me
as unintuitive and going against the intuition users build up from
common concrete manifestations of '<', such as numeric types.

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4367.html
or

might be interesting background.

···

on Wed Mar 09 2016, Joe Groff <jgroff-AT-apple.com> wrote:

On Mar 9, 2016, at 11:41 AM, Dave Abrahams <dabrahams@apple.com> wrote:

--
-Dave

Auto-deriving is a different story, though, especially if it's opt-in
(you have to say `deriving Equatable`). There, you presumably have
looked at the default semantics and determined they're appropriate for
your type.

I don't know if that level of explicitness is needed. Explicit
declaration of conformance might be enough.

I don't know if it's necessary either. What I'm saying is that if it *is* that explicit, it's a slam dunk; if it's *not* that explicit, it's a little more uncertain, but probably still a good idea.

···

--
Brent Royal-Gordon
Architechies

This is something I've wanted for a while (ever since basic equality was
added to tuples), and I've considered writing a proposal but never had the
time to jot something down. I'm glad other people have had the same
thoughts. I've become frustrated at having to write the switch-case-let
boilerplate for enums, especially.

I'd like to see the following rules:

For structs:
- A struct should implicitly conform* to Equatable if all of its fields
conform (either explicitly or implicitly by these rules) to Equatable. The
compiler will auto-generate an implementation that is simply the AND of the
field-wise comparisons.
- A struct should implicitly conform to Hashable if all of its fields
conform (exp. or imp.) to Hashable. The compiler will auto-generate an
implementation that computes a hash value using a universal hash function
(say, Knuth's) based on the hash values of its fields. (Order matters here,
so declaration order?)

For enums:
- An enum should implicitly conform to Equatable if all of the types of all
associated values across all cases conform to Equatable. The compiler will
auto-generate an implementation that switches on the case and if they are
the same, compares the associated values.
- An enum should implicitly conform to Hashable if all of its fields
conform (exp. or imp.) to Hashable. The compiler will auto-generate an
implementation that computes a hash value using a universal hash value
where the first term is the case's ordinal (position in declaration order)
followed by terms coming from its associated values' hash values.

I'm not 100% sold on Comparable being a good idea to support here; when
implementing Comparable over multiple fields, there's often a specific
order that matters—you encounter a pair of fields that are equal so you
move on to the next one. I don't think we want to tie that to field
declaration order or anything like that... it seems to surprising. But it
would be nice to get rid of that boilerplate as well.

* When I say "implicitly conform" above, I'm torn about whether that should
mean:
- the struct or enum doesn't have to list Equatable/Hashable at all, but it
gets it anyway (kind of like how enums that extend a scalar type get
RawRepresentable automatically), or
- the struct or enum gets implicit implementations of ==/hashValue, but you
still have to explicitly declare that it conforms to Equatable/Hashable

On the one hand, getting the conformance implicitly matches how
RawRepresentable is already treated, and makes getting the conformance zero
effort. On the other hand, it makes error handling trickier: if you add a
non-Equatable/non-Hashable field or associated value to a struct/enum, all
of your call sites break. (I guess they would even if you declared the
conformance, but at least there you'd also get the "Type does not conform
to Equatable/Hashable" error at the struct/enum itself, and the compiler
could even potentially flag the offending field/associated value that's
causing the conformance to fail.) Thoughts?

If there's interest, I'd be happy to put this into the actual proposal
template.

···

On Tue, May 24, 2016 at 8:46 AM Mark Sands via swift-evolution < swift-evolution@swift.org> wrote:

I apologize upfront for necromancing this thread. Is there any momentum on
this proposal?

My team was discussing the merits of default (opt-in) Equality. As soon as
the equality function has been written then the compiler is satisfied, but
there is no way to get compile time correctness for the equality. Unit
tests can only go so far with ensuring the equality function is robust.
Once a new property is added to a struct, for instance, the equality
function is no longer correct and without warning. Currently there is no
way to safeguard this situation. This would be a welcome addition to Swift.

Mark

On Fri, Mar 11, 2016 at 11:19 AM, Jose Cheyo Jimenez via swift-evolution < > swift-evolution@swift.org> wrote:

> (starting a new thread by DaveA's request)
>
> There's a definition of equality that makes sense as a default for
nearly every type in our system:
>
> - Basic types like IntNN, FloatNN, String, etc. have domain-defined
equality,
> - Structs and tuples can be considered equal if their corresponding
fields are equal,
> - Enums can be considered equal if they carry the same, equal payload,

+1 for Equality, Comparable and Hashable especially for enums and tuples.

I know that equality was added for tuples but I think that enums are
perfect for adding automatic generation of conformance to Equality,
Comparable and Hashable.

Take a look at this toy example of an Enum with 9 cases

boiler plate to conform to Equatable

https://github.com/exercism/xswift/blob/master/exercises/poker/PokerExample.swift#L151-L189

boiler plate to conform to Comparable

https://github.com/exercism/xswift/blob/master/exercises/poker/PokerExample.swift#L191-L245

> It's my feeling that Equatable and Hashable would make a lot of sense
as universal operations; I'm not so sure about Comparable.
>
> -Joe

For enums a defaultComparable protocol could just rank on the order the
fields are declared:

https://github.com/exercism/xswift/blob/master/exercises/poker/PokerExample.swift#L211-L212

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

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

Generally a -1 here. We have to be very careful.

Take Java as a worst case example; very object is hashable and it makes hashability almost meaningless as a constraint. Did the object actually implement a hash function appropriate to itself or is it relying on the default implementation? Any mechanism using hashability is at best fragile. This can get particularly difficult when the hashing usage of the class is behind an api and the class implementer has to understand that - despite not being required by the compiler - they need to provide an acceptable override of the hashing mechanism.

Java’s default hash function will only succeed against the exact same instance and this was probably chosen because it is very conservative and has no performance implications. But it is almost always not what a object that actually requires to be hashable will want.

So it comes down to the default implementation and I would argue that it might be impossible to provide a default implementation good enough particularly when you consider performance. The alternative to Java’s conservative same-instance hashability is some kind of memberwise implementation which could silently become a very expensive calculation (maybe unnecessarily so and even then might not accurately represent the hashablity/equality of the instance).

I think there could be some potential to provide a way to annotate a type to indicate what members should make up the hash/equality (and if they are all immutable, the hash could be pre-computed on instantiation) to avoid having to write boilerplate but it shouldn’t be an innate feature every type.

-Simon

···

On 9 Mar 2016, at 5:51 AM, Step C via swift-evolution <swift-evolution@swift.org> wrote:

I find it valuable to think explicitly about what equality means for my types, though not so valuable to write a bunch of boilerplate to support it. Derivable or automatic conformances might also carry us further towards users being able to provide their own automagic conformances.

- Class references can be considered equal if they refer to the same instance,

With opt-in conformance we could potentially have field comparison for classes too. I guess that would still need to be a customization point regardless as neither approach is always the right answer.

On Mar 9, 2016, at 6:29 AM, Haravikk via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

While I appreciate the idea behind the proposal, I think I’m a -1 to it. Java has required equality and hashable as part of its base Object class, but I frequently encountered classes that had very poor implementations for these, or never bothered to provide one; arguably they didn’t need to, which is fine, but it kind of went against the whole idea.

Swift has some pretty nifty features that also make this redundant, for example, I’ve been working on some ordered collection types; my natural inclination was to require that values be Comparable, however this actually limits the usefulness of the collection (or requires values to be wrapped somehow). Instead I decided to accept values of any type, and also take a closure (same as used to sort an array).

However, with generic constraints I can still provide a default closure for Comparable types like so:

// Sort Comparable elements in ascending order if no closure is provided.
extension OrderedCollection where Self.Generator.Element:Comparable {
  init<S:SequenceType where S.Generator.Element == Generator.Element>(elements:S) {
    self.init(isOrderedBefore: { $0 < $1 }, elements: elements)
  }
}

(the same feature also lets me implement ArrayLiteralConvertible for Comparable arrays, though I have to provide a default initialiser producing a fatal error for the rest)

It’s a bit of a weird thing to get your head around at first, but you can solve a lot of problems in a similar way, without having to place overly strict requirements on the types that you can accept, removing the need for all types to conform to anything.

On 9 Mar 2016, at 08:30, Austin Zheng via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

As Brent pointed out, adding this sort of support opens a whole can of worms. Large parts of the standard library would silently become unsound.

As well, in my experience people who have had trouble using (e.g.) Equatable with heterogeneous collections are often trying to do type-unsound things. Maybe Swift should support a separate notion of heterogenous equality for comparisons between Equatable types (and one of the POP WWDC talks actually sketched out an outline of how this might be done), but that's different from making Equatable universal. In addition, I think Swift 3's proposed support for conditional protocol conformance will make creating principled heterogeneous collections easier, which should ease some of the burden.

Best,
Austin

On Mar 9, 2016, at 12:17 AM, David Hart <david@hartbit.com <mailto:david@hartbit.com>> wrote:

On 08 Mar 2016, at 23:15, Austin Zheng via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I would prefer Equatable and Hashable to remain opt-in, and for us to add better support for automatic deriving of implementation.

On 08 Mar 2016, at 23:57, Zach Waldowski via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I completely agree with Austin here. Automatic derivation (perhaps through the same mechanisms Joe is talking about) would be a nice enhancement, but I find it refreshing and advantageous for simple value types to have very little automatic behavior.

Pedantically I agree with both of you, but from a very pragmatic point of you, I think it's very important to point out what Joe said about how this could reduce one of the most frustrating aspects of Swift, when people work with heterogeneous arrays and try to conform to Equatable:

that would solve many of the common problems people currently have trying to work with heterogeneous containers.

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

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

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

I find it valuable to think explicitly about what equality means for my types, though not so valuable to write a bunch of boilerplate to support it. Derivable or automatic conformances might also carry us further towards users being able to provide their own automagic conformances.

- Class references can be considered equal if they refer to the same instance,

With opt-in conformance we could potentially have field comparison for classes too. I guess that would still need to be a customization point regardless as neither approach is always the right answer.

···

On Mar 9, 2016, at 6:29 AM, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

While I appreciate the idea behind the proposal, I think I’m a -1 to it. Java has required equality and hashable as part of its base Object class, but I frequently encountered classes that had very poor implementations for these, or never bothered to provide one; arguably they didn’t need to, which is fine, but it kind of went against the whole idea.

Swift has some pretty nifty features that also make this redundant, for example, I’ve been working on some ordered collection types; my natural inclination was to require that values be Comparable, however this actually limits the usefulness of the collection (or requires values to be wrapped somehow). Instead I decided to accept values of any type, and also take a closure (same as used to sort an array).

However, with generic constraints I can still provide a default closure for Comparable types like so:

// Sort Comparable elements in ascending order if no closure is provided.
extension OrderedCollection where Self.Generator.Element:Comparable {
  init<S:SequenceType where S.Generator.Element == Generator.Element>(elements:S) {
    self.init(isOrderedBefore: { $0 < $1 }, elements: elements)
  }
}

(the same feature also lets me implement ArrayLiteralConvertible for Comparable arrays, though I have to provide a default initialiser producing a fatal error for the rest)

It’s a bit of a weird thing to get your head around at first, but you can solve a lot of problems in a similar way, without having to place overly strict requirements on the types that you can accept, removing the need for all types to conform to anything.

On 9 Mar 2016, at 08:30, Austin Zheng via swift-evolution <swift-evolution@swift.org> wrote:

As Brent pointed out, adding this sort of support opens a whole can of worms. Large parts of the standard library would silently become unsound.

As well, in my experience people who have had trouble using (e.g.) Equatable with heterogeneous collections are often trying to do type-unsound things. Maybe Swift should support a separate notion of heterogenous equality for comparisons between Equatable types (and one of the POP WWDC talks actually sketched out an outline of how this might be done), but that's different from making Equatable universal. In addition, I think Swift 3's proposed support for conditional protocol conformance will make creating principled heterogeneous collections easier, which should ease some of the burden.

Best,
Austin

On Mar 9, 2016, at 12:17 AM, David Hart <david@hartbit.com> wrote:

On 08 Mar 2016, at 23:15, Austin Zheng via swift-evolution <swift-evolution@swift.org> wrote:

I would prefer Equatable and Hashable to remain opt-in, and for us to add better support for automatic deriving of implementation.

On 08 Mar 2016, at 23:57, Zach Waldowski via swift-evolution <swift-evolution@swift.org> wrote:

I completely agree with Austin here. Automatic derivation (perhaps through the same mechanisms Joe is talking about) would be a nice enhancement, but I find it refreshing and advantageous for simple value types to have very little automatic behavior.

Pedantically I agree with both of you, but from a very pragmatic point of you, I think it's very important to point out what Joe said about how this could reduce one of the most frustrating aspects of Swift, when people work with heterogeneous arrays and try to conform to Equatable:

that would solve many of the common problems people currently have trying to work with heterogeneous containers.

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

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

I am +1 on being able to opt in for a derived `hashValue` and an emphatic
+1 to the idea of being able to make functions conform to `Equatable` and
`Hashable` at all. I agree with the sentiment that it should still be opt
in, however.

TJ

···

On Wed, Mar 9, 2016 at 8:51 AM, Step C via swift-evolution < swift-evolution@swift.org> wrote:

I find it valuable to think explicitly about what equality means for my
types, though not so valuable to write a bunch of boilerplate to support
it. Derivable or automatic conformances might also carry us further towards
users being able to provide their own automagic conformances.

- Class references can be considered equal if they refer to the same
instance,

With opt-in conformance we could potentially have field comparison for
classes too. I guess that would still need to be a customization point
regardless as neither approach is always the right answer.

On Mar 9, 2016, at 6:29 AM, Haravikk via swift-evolution < > swift-evolution@swift.org> wrote:

While I appreciate the idea behind the proposal, I think I’m a -1 to it.
Java has required equality and hashable as part of its base Object class,
but I frequently encountered classes that had very poor implementations for
these, or never bothered to provide one; arguably they didn’t need to,
which is fine, but it kind of went against the whole idea.

Swift has some pretty nifty features that also make this redundant, for
example, I’ve been working on some ordered collection types; my natural
inclination was to require that values be Comparable, however this actually
limits the usefulness of the collection (or requires values to be wrapped
somehow). Instead I decided to accept values of any type, and also take a
closure (same as used to sort an array).

However, with generic constraints I can still provide a default closure
for Comparable types like so:

// Sort Comparable elements in ascending order if no closure is provided.
extension OrderedCollection where Self.Generator.Element:Comparable {
init<S:SequenceType where S.Generator.Element ==
Generator.Element>(elements:S) {
self.init(isOrderedBefore: { $0 < $1 }, elements: elements)
}
}

(the same feature also lets me implement ArrayLiteralConvertible for
Comparable arrays, though I have to provide a default initialiser producing
a fatal error for the rest)

It’s a bit of a weird thing to get your head around at first, but you can
solve a lot of problems in a similar way, without having to place overly
strict requirements on the types that you can accept, removing the need for
all types to conform to anything.

On 9 Mar 2016, at 08:30, Austin Zheng via swift-evolution < > swift-evolution@swift.org> wrote:

As Brent pointed out, adding this sort of support opens a whole can of
worms. Large parts of the standard library would silently become unsound.

As well, in my experience people who have had trouble using (e.g.)
Equatable with heterogeneous collections are often trying to do
type-unsound things. Maybe Swift should support a separate notion of
heterogenous equality for comparisons between Equatable types (and one of
the POP WWDC talks actually sketched out an outline of how this might be
done), but that's different from making Equatable universal. In addition, I
think Swift 3's proposed support for conditional protocol conformance will
make creating principled heterogeneous collections easier, which should
ease some of the burden.

Best,
Austin

On Mar 9, 2016, at 12:17 AM, David Hart <david@hartbit.com> wrote:

On 08 Mar 2016, at 23:15, Austin Zheng via swift-evolution < > swift-evolution@swift.org> wrote:

I would prefer Equatable and Hashable to remain opt-in, and for us to add
better support for automatic deriving of implementation.

On 08 Mar 2016, at 23:57, Zach Waldowski via swift-evolution < > swift-evolution@swift.org> wrote:

I completely agree with Austin here. Automatic derivation (perhaps through
the same mechanisms Joe is talking about) would be a nice enhancement, but
I find it refreshing and advantageous for simple value types to have very
little automatic behavior.

Pedantically I agree with both of you, but from a very pragmatic point of
you, I think it's very important to point out what Joe said about how this
could reduce one of the most frustrating aspects of Swift, when people work
with heterogeneous arrays and try to conform to Equatable:

that would solve many of the common problems people currently have trying
to work with heterogeneous containers.

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

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

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

IMO, since you can already get "implicitly derived" default implementations from a protocol extension simply by declaring conformance to a protocol, it would be consistent to only require a conformance declaration to access compiler-derived implementations. One could imagine Swift Pro 6s Plus having enough fancy generics features to provide the default implementations of Equatable, Hashable, etc. as protocol extensions.

-Joe

···

On Mar 9, 2016, at 5:57 PM, Brent Royal-Gordon <brent@architechies.com> wrote:

Auto-deriving is a different story, though, especially if it's opt-in
(you have to say `deriving Equatable`). There, you presumably have
looked at the default semantics and determined they're appropriate for
your type.

I don't know if that level of explicitness is needed. Explicit
declaration of conformance might be enough.

I don't know if it's necessary either. What I'm saying is that if it *is* that explicit, it's a slam dunk; if it's *not* that explicit, it's a little more uncertain, but probably still a good idea.

Presumably a DefaultValueHashable protocol with default implementation of
Hashable and Equatable for value types and similarly DefaultObjectHashable
for objects could be provided in the standard library (using some deep
integration into the compiler or a to be added reflection ability). Then it
would be easy to opt in.

···

On Thursday, 10 March 2016, Sean Heber via swift-evolution < swift-evolution@swift.org> wrote:

> We can also allow opt-in by adopting a protocol, eg `struct
SimpleStruct: DefaultEquatable`. DefaultEquatable would tell the compiler
to auto-generate an equatable implementation. Similar for DefaultHashable.
>
> In the C# world you’d actually do something like this with an attribute.
I know user-defined attributes aren’t on the table right now but some more
built-in ones would be sufficient: @defaultEquatable, @defaultHashable, etc.
> They could even take a list of properties to ignore if such a feature
were useful: `@defaultEquatable(ignored: [currentDate, randomInt])`.

Might be interesting if you could pass in parameters when you declare
conformance, like:

struct MyType : Equatable(default) {}

If “default” was missing, you’d have to supply the relevant ==/equals
function or whatever as usual, but if it was there you’d get automatically
generated stuff based on documented rules.

Maybe it could even be extensible so you can define protocol extensions
that are “named” and only apply if you declare conformance with the
relevant name:

protocol Bar {
  func barFunction()
}

extension Bar(foo) {
  func fooFunction() { }
}

struct Type1 : Bar(foo) {
  func barFunction() {}
}

struct Type2 : Bar {
  func barFunction() {}
}

A Type1 instance would also have a fooFunction(), but a Type2 instance
would not.

Using this you could have multiple default implementations defined for the
same protocol depending on circumstances.

extension Bar(scenerio1) {
  func barFunction() { print(“1”) }
}

extension Bar(scenerio2) {
  func barFunction() { print(“2") }
}

struct Thing1 : Bar(scenerio1) {}
struct Thing2 : Bar(scenerio2) {}

let a: Bar = Thing1()
let b: Bar = Thing2()

a.barFunction() -> “1”
b.barFunction() -> “2”

:)

l8r
Sean

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <javascript:;>
https://lists.swift.org/mailman/listinfo/swift-evolution

--
-- Howard.

I think we might (at least partially) be in violent agreement :). Most (if not everyone) on this thread has agreed that painless opt-in auto-conformance is a good thing ("struct Foo : Regular { .. }"), albeit with differing definitions of 'painless'. But I maintain that having a "func ==(lhs: Any, rhs: Any) -> Bool" stdlib fallback implementation of == is a lot of potential pain for very little benefit.

I agree completely. Not having everything comparable with everything is a good thing IMO!

-Thorsten

···

Am 09.03.2016 um 22:34 schrieb Austin Zheng via swift-evolution <swift-evolution@swift.org>:

Austin

On Wed, Mar 9, 2016 at 1:17 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:

on Tue Mar 08 2016, Brent Royal-Gordon <brent-AT-architechies.com> wrote:

>> - Function types in Swift do not provide a ready equality
> operation. We could provide a default implementation that always
> returns 'false', perhaps.
>
> I think this sort of puts the lie to the idea.
>
> We can always provide *a* definition of equality, but I suspect it
> will often be an *incorrect* definition.

I disagree; IMO it would seldom be incorrect.
But I wouldn't necessarily want to make everything equatable. I'd want
to give many things an equatable conformance that's available simply by
declaring it. In fact, I'd like to be able to say:

          struct Something : Regular {
              // Stored properties that are all Regular
          }

and get Equatable, Comparable, and Hashable for free.

> That's why you had to suggest functions should always be false: you
> cannot (without more effort than you want to spend) provide a correct
> definition of equality for it.
>
> I mean, imagine what happens if you make functions Equatable and
> Hashable but with definitions that don't actually work. Currently,
> `Set<Void -> Void>` gives you an error:
>
> error: type 'Void -> Void' does not conform to protocol 'Hashable'
>
> But with this feature in place, Swift would happily produce a Set of
> functions which collides endlessly, doesn't do any uniquing, never
> says it contains any value you pass into it, and can only remove
> elements by index.
>
> A type that is "never equal" completely breaks Set in practice, and
> there's no way for the type system to catch the problem.
>
> If we automatically synthesize a == operator for every type, many of
> those operators will be incorrect. For instance, anything that
> includes a cache will be incorrect.
> Anything that includes a pointer to a buffer and ought to evaluate the
> buffer's contents will be incorrect.
> Anything that includes a closure will be incorrect. Individually, each
> of these cases is minor, but they multiply and interact with each
> other until, together, they undermine confidence in ==.

These things wouldn't be Regular by themselves. To make them
composable, you can create a single Regular value type that wraps each
one.

> If you explicitly mark things as Equatable, then it is clear that the
> equality operator on them really does have a sensible definition. But
> if you can pass anything into ==, you will never know what will
> actually *work*. If everything's Equatable, then nothing is.
>
> * * *
>
> Auto-deriving is a different story, though, especially if it's opt-in
> (you have to say `deriving Equatable`). There, you presumably have
> looked at the default semantics and determined they're appropriate for
> your type.

I don't know if that level of explicitness is needed. Explicit
declaration of conformance might be enough.

> But I think it's clear that derived conformances should eventually be
> a user-accessible feature. We already derive RawRepresentable and
> ErrorType on enums. (We also derive initializers on some types, but
> that's arguably a separate feature.) I think it's clear that Swift ∞
> ought to allow you to derive protocol conformances; it's just a matter
> of scheduling.
>
> So I think that what we ought to do is this:
>
> • Make a best guess at what Swift ∞ would want you to do to invoke the
> user-specified derivation logic for an arbitrary protocol—implicit
> derivation or something marked by a keyword.
> • Think about whether derived conformances of Equatable, Hashable,
> and/or Comparable are urgent enough that we should implement them
> before the general feature.
> • If so, design these features along the lines of what we would expect
> the eventual user-specified derivation feature to use.

Of course.

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

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

I can’t tell if I misunderstood Joe's original proposal, but I don’t think the suggestion was to have everything comparable to everything, but to have any type comparable with itself by default. I’m against everything being comparable with everything else, but for having a default equality for every type with itself.

To re-state:

-1 to: func ==(lhs: Any, rhs: Any) -> Bool
+1 to: func ==<T> (lhs: T, rhs: T) -> Bool

- Will

···

On Mar 10, 2016, at 9:58 PM, Thorsten Seitz via swift-evolution <swift-evolution@swift.org> wrote:

Am 09.03.2016 um 22:34 schrieb Austin Zheng via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:

I think we might (at least partially) be in violent agreement :). Most (if not everyone) on this thread has agreed that painless opt-in auto-conformance is a good thing ("struct Foo : Regular { .. }"), albeit with differing definitions of 'painless'. But I maintain that having a "func ==(lhs: Any, rhs: Any) -> Bool" stdlib fallback implementation of == is a lot of potential pain for very little benefit.

I agree completely. Not having everything comparable with everything is a good thing IMO!

I took Joe's points about making it easier to work with heterogeneous collections to refer to heterogeneous comparison.

I'm still -1 to "func ==<T>(lhs: T, rhs: T) -> Bool", but for the reasons Brent brought up earlier. If adding a default conformance can be made "as easy as" declaring conformance to "Equatable" without an explicit impl (or declaring conformance to "Regular" or "#derives(Equatable)" or ...), I see that as the preferable option:

- The amount of inconvenience it gives the programmer is negligible - one conformance declaration for each custom type that deserves to be equatable, and no function or operator code.
- Declaring it explicitly is a flag that the programmer has intentionally decided that this type should be equatable under whatever common-sense default rules the compiler uses for the type's 'kind'. This is especially important for APIs being vended out, whose implementations might be opaque to the user.
- Declaring a universal homogenous equality operator that works for types for which equality makes no sense (or there's no clear definition) is actively hostile to users of the language (to go back to Brent's point, "why is my Set<() -> Int> broken when I try using it?")

Austin

···

On Mar 10, 2016, at 10:21 PM, William Dillon <william@housedillon.com> wrote:

On Mar 10, 2016, at 9:58 PM, Thorsten Seitz via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Am 09.03.2016 um 22:34 schrieb Austin Zheng via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:

I think we might (at least partially) be in violent agreement :). Most (if not everyone) on this thread has agreed that painless opt-in auto-conformance is a good thing ("struct Foo : Regular { .. }"), albeit with differing definitions of 'painless'. But I maintain that having a "func ==(lhs: Any, rhs: Any) -> Bool" stdlib fallback implementation of == is a lot of potential pain for very little benefit.

I agree completely. Not having everything comparable with everything is a good thing IMO!

I can’t tell if I misunderstood Joe's original proposal, but I don’t think the suggestion was to have everything comparable to everything, but to have any type comparable with itself by default. I’m against everything being comparable with everything else, but for having a default equality for every type with itself.

To re-state:

-1 to: func ==(lhs: Any, rhs: Any) -> Bool
+1 to: func ==<T> (lhs: T, rhs: T) -> Bool

- Will