[swift-evolution] Universal Equatability, Hashability, and Comparability

Dave Abrahams dabrahams at apple.com
Wed Mar 9 15:17:36 CST 2016


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


More information about the swift-evolution mailing list