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

William Dillon william at housedillon.com
Wed Mar 9 10:40:45 CST 2016


I’m +1 to this idea for Hashable and Equatable. 

I really hate writing hashValue implementations, and equality is something that I wouldn’t miss.  The way Joe proposes these would be implemented is already very close to what I do anyway.  I don’t see how it would have a negative effect on existing code.  Existing code that uses ‘==‘ or ‘hashValue' necessarily implements these functions, and they would override the default implementation.

It seems to me that the biggest risk would be if you try to compare two instances (using ==, for example) and you haven’t overridden the default implementation.  If you have a weird type, and get unexpected behavior, it may be hard to remember that ‘==‘ might be to blame.  With the current status quo, Swift would just say “no” and you’d be on your way to writing a new implementation.  With this proposal, it might take a bit more time to understand what’s happening.  However, I doubt that the comparison of (really?) weird types are very frequent.  Furthermore, if you do have something that is that weird, as you’re typing “==“ you’re probably thinking “This is a really weird type, I should probably make sure that the default ‘==‘ makes sense.”

In my opinion, making it opt-in would introduce relatively weird or inelegant syntax to mark your type as opting into the behavior.  I’d rather allow for overridden behavior.

Finally, I don’t think Comparable is a good idea for this. 

- Will

> On Mar 8, 2016, at 12:54 PM, Joe Groff via swift-evolution <swift-evolution at 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,
> - Class references can be considered equal if they refer to the same instance,
> - Metatypes can be considered equal if they represent the same type, and
> - Existentials can be considered equal if they carry equal values of the same dynamic type.
> 
> and similarly, reasonable hash code implementations could be synthesized by applying a standard hash combine operation over the components, and a default ordering  could be assigned to values of every type. I think it's worth considering whether Equatable, Hashable, and/or Comparable, instead of being explicit protocols, should become universal behavior like 'print', with customization points to override the default behavior. If Equatable and Hashable behavior were universal, that would solve many of the common problems people currently have trying to work with heterogeneous containers. In object-oriented frameworks, including Cocoa, Java, and .NET, it is common for the root (NS)Object class to provide default equality and hashing operations. There are of course some tradeoffs:
> 
> - Universal behavior would require us to either generate code for '==', 'hashValue', and/or '<' for every type, or provide sufficient reflection info for a common runtime implementation to do it. The reflection-based approach may be reasonable for print(), since dumping reflection info only reduces the quality of the default logging behavior, but '==' and 'hashValue' are more essential to proper behavior, so relying on reflection might be too slow, and would be brittle when we introduce the ability to drop reflection info.
> - Type safety with '==' is important to prevent accidental '1 == "1"' type comparsions, and a fully generic 'func ==<T>(x: T, y: T) -> Bool' could potentially allow those sorts of mixed-type comparisons by accident. Language rules that constrained when generic parameters can be resolved to supertypes might help here.
> - Function types in Swift do not provide a ready equality operation. We could provide a default implementation that always returns 'false', perhaps.
> - A Comparable ordering can be dreamt up for many types, but it's not always a stable ordering, or a desired one. 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. That might be good enough for ordered collections like search trees, but is weaker than what many people expect '<' to do.
> 
> 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
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution



More information about the swift-evolution mailing list