If an extension on your type declares a hashValue property, what should be the semantics? Is that an error (conflicts with default provided one), or is that one used?
> On May 4, 2017, at 3:20 PM, Andrew Bennett via swift-evolution > <[email protected]> wrote: > > That's correct, consistent with Encoding/Decoding (SE-0166 > <https://github.com/apple/swift-evolution/blob/master/proposals/0166-swift-archival-serialization.md>) > it is sufficient to just do this: > > struct MyType: Hashable { > var foo: Int > var bar: Float > } > > Now MyType should get a generated implementation because each member is also > Hashable. > > There's more details in the proposal near: > An enum T that derives Equatable will receive a compiler-generated > implementation of > > > > On Fri, May 5, 2017 at 8:15 AM, Xiaodi Wu <[email protected] > <mailto:[email protected]>> wrote: > I'm imagining no syntax; effectively, as though there exists an "extension > Equatable where [ all members : Equatable ]" with a default implementation. > > > On Thu, May 4, 2017 at 17:13 John McCall <[email protected] > <mailto:[email protected]>> wrote: >> On May 4, 2017, at 6:10 PM, Andrew Bennett via swift-evolution >> <[email protected] <mailto:[email protected]>> wrote: >> I agree, let's make it opt-in. >> >> This looks really great, I'm excited to get generated conformance for >> Equatable/Hashable in time for Swift 4. >> >> I think it's worth mentioning that Encoding/Decoding generated conformance >> is already accepted and implemented in Swift 4. The implementation and >> acceptance criterion for Equatable/Hashable is likely to be very similar. >> >> For the open questions, I think for the sake of getting this into Swift 4 we >> should go for explicit derivation, and don't allow omission of fields (yet). > > Is there a syntax proposal for explicit derivation? Or are you imagining > that just declaring the conformance but failing to implement it should > trigger derivation? > > John. > >> >> Omission is nice-to-have, but likely to be a long-winded bike-shed. >> >> Changing from explicit to implicit is a loss of information, and has a large >> impact on the language, it can't easily be undone, so it requires a large >> discussion when it's decided. It only adds a little additional convenience >> to the user though. >> >> I suggest we discuss implicit generation and allowing omission with >> follow-up proposals, they will very likely be additive non-breaking changes. >> For this proposal we play it safe and stick to explicit conformance and no >> omission of fields. >> >> >> On Fri, May 5, 2017 at 8:01 AM, Xiaodi Wu via swift-evolution >> <[email protected] <mailto:[email protected]>> wrote: >> Hmm, I can see the appeal of automatically deriving Equatable and Hashable >> conformance, but I'd like that to be opt-in. That is, types should declare >> that they are Equatable or Hashable to begin with. It wouldn't have to take >> extra syntax, as compiler magic could effectively synthesize default >> implementations for == and/or hashValue when all members are themselves >> Equatable or Hashable, respectively. With such a scheme, consideration can >> be made to accommodating classes too. >> On Thu, May 4, 2017 at 15:37 Tony Allevato via swift-evolution >> <[email protected] <mailto:[email protected]>> wrote: >> Hi all, >> >> A conversation on Twitter last night brought up some interest in this >> feature and I was encouraged to revive this proposal. >> >> Jordan Rose mentioned >> <https://twitter.com/UINT_MIN/status/859922619578986496> on Twitter that it >> could possibly make it in by the Swift 4 deadline if others contributed—I >> have a WIP branch (albeit not currently working because I rebased after a >> couple months of it being idle) that does the work for enums but I got stuck >> on the mutually recursive cases. If this got approved, I'd love to >> collaborate with other interested folks to finish up the implementation. >> >> Link: https://gist.github.com/allevato/2fd10290bfa84accfbe977d8ac07daad >> <https://gist.github.com/allevato/2fd10290bfa84accfbe977d8ac07daad> >> >> >> Deriving Equatable and Hashable for value types >> >> Proposal: SE-0000 >> <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md> >> Author(s): Tony Allevato <https://github.com/allevato> >> Status: Awaiting review >> <https://gist.github.com/allevato/2fd10290bfa84accfbe977d8ac07daad#rationale> >> Review manager: TBD >> >> <https://gist.github.com/allevato/2fd10290bfa84accfbe977d8ac07daad#introduction>Introduction >> >> Value types are prevalent throughout the Swift language, and we encourage >> developers to think in those terms when writing their own types. Frequently, >> developers have to write large amounts of boilerplate code to support >> equatability and hashability of value types. This proposal offers a way for >> the compiler to automatically derive conformance to Equatable and Hashable >> to reduce this boilerplate, in a subset of scenarios where generating the >> correct implementation is known to be possible. >> >> Swift-evolution thread: Universal Equatability, Hashability, and >> Comparability <http://thread.gmane.org/gmane.comp.lang.swift.evolution/8919> >> >> <https://gist.github.com/allevato/2fd10290bfa84accfbe977d8ac07daad#motivation>Motivation >> >> Building robust value types in Swift can involve writing significant >> boilerplate code to support hashability and equatability. Equality is >> pervasive across many value types, and for each one users must implement the >> == operator such that it performs a fairly rote memberwise equality test. As >> an example, an equality test for a struct looks fairly uninteresting: >> >> struct Foo: Equatable { >> static func == (lhs: Foo, rhs: Foo) -> Bool { >> return lhs.property1 == rhs.property1 && >> lhs.property2 == rhs.property2 && >> lhs.property3 == rhs.property3 && >> ... >> } >> } >> What's worse is that this operator must be updated if any properties are >> added, removed, or changed, and since it must be manually written, it's >> possible to get it wrong, either by omission or typographical error. >> >> Likewise, hashability is necessary when one wishes to store a value type in >> a Set or use one as a multi-valued Dictionary key. Writing high-quality, >> well-distributed hash functions is not trivial so developers may not put a >> great deal of thought into them – especially as the number of properties >> increases – not realizing that their performance could potentially suffer as >> a result. And as with equality, writing it manually means there is the >> potential to get it wrong. >> >> In particular, the code that must be written to implement equality for enums >> is quite verbose: >> >> enum Token: Equatable { >> case string(String) >> case number(Int) >> case lparen >> case rparen >> >> static func == (lhs: Token, rhs: Token) -> Bool { >> switch (lhs, rhs) { >> case (.string(let lhsString), .string(let rhsString)): >> return lhsString == rhsString >> case (.number(let lhsNumber), .number(let lhsNumber)): >> return lhsNumber == rhsNumber >> case (.lparen, .lparen), (.rparen, .rparen): >> return true >> default: >> return false >> } >> } >> } >> Crafting a high-quality hash function for this enum would be similarly >> inconvenient to write. >> >> Swift already derives Equatable and Hashable conformance for a small subset >> of enums: those for which the cases have no associated values (including >> enums with raw types). Two instances of such an enum are equal if they are >> the same case, and an instance's hash value is its ordinal: >> >> enum Foo { >> case zero, one, two >> } >> >> let x = (Foo.one == Foo.two) // evaluates to false >> let y = Foo.one.hashValue // evaluates to 1 >> Likewise, conformance to RawRepresentable is automatically derived for enums >> with a raw type. Since there is precedent for derived conformances in Swift, >> we propose extending this support to more value types. >> >> >> <https://gist.github.com/allevato/2fd10290bfa84accfbe977d8ac07daad#proposed-solution>Proposed >> solution >> >> In general, we propose that value types derive conformance to >> Equatable/Hashable if all of its members are Equatable/Hashable. We describe >> the specific conditions under which these conformances are derived below, >> followed by the details of how the conformance requirements are implemented. >> >> >> <https://gist.github.com/allevato/2fd10290bfa84accfbe977d8ac07daad#protocol-derivability-conditions>Protocol >> derivability conditions >> >> For brevity, let P represent either the protocol Equatable or Hashable in >> the descriptions below. >> >> >> <https://gist.github.com/allevato/2fd10290bfa84accfbe977d8ac07daad#derived-conformances-for-enums>Derived >> conformances for enums >> >> For an enum, derivability of P is based on the conformances of its cases' >> associated values. Computed properties are not considered. >> >> The following rules determine whether conformance to P can be derived for an >> enum: >> >> An enum with no cases does not derive conformance to P, since it is not >> possible to create instances of such types. >> >> An enum with one or more cases derives conformance to P if all of the >> associated values of all of its cases conform to P (either explicitly or >> derived). >> >> >> <https://gist.github.com/allevato/2fd10290bfa84accfbe977d8ac07daad#derived-conformances-for-structs>Derived >> conformances for structs >> >> For a struct, derivability of P is based on the conformances of its stored >> instance properties only. Neither static properties nor computed instance >> properties (those with custom getters) are considered. >> >> The following rules determine whether conformance to P can be derived for a >> struct: >> >> A struct with no stored properties does not derive conformance to P. (Even >> though it is vacuously true that all instances of a struct with no stored >> properties could be considered equal and hash to the same value, the reality >> is that such structs are more often used for grouping/nesting of other >> entities and not for their singular value, and we don't consider it >> worthwhile to generate extra code in this case.) >> >> A struct with one or more stored properties derives conformance to P if all >> if the types of all of its stored properties conform to P (either explicitly >> or derived). >> >> >> <https://gist.github.com/allevato/2fd10290bfa84accfbe977d8ac07daad#considerations-for-recursive-types>Considerations >> for recursive types >> >> For brevity in the discussion below, the term members refers only to those >> members that are checked for deriving conformances: stored properties for >> structs and associated values for enums. >> >> Recursive value types require a bit more care when determining whether a >> conformance can be derived. Consider the following enum with an indirect >> case: >> >> enum TreeNode { >> case empty >> case leaf(value: Int) >> case internal(left: TreeNode, right: TreeNode) >> } >> When examining the internal case, an application of the rules above implies >> that "TreeNode derives P if TreeNode conforms to P"—a recursive condition. >> In this situation, we note that any instance of this type—or of any >> recursive type—forms a finite tree structure because the recursion must be >> terminated eventually by using one of the other base cases. Therefore, >> without changing the outcome, we can assume for the purposes of determining >> whether T derives P that any members of type T already conform to P. If any >> members of different types prohibit deriving P, then we know that the whole >> type cannot derive it; likewise, if all of the other members permit deriving >> P, then we know that T can derive it by recursively applying the derived >> operation. >> >> This property can be extended to mutually recursive types as well. Consider >> this contrived example: >> >> enum A { >> case value(Int) >> case b(B) >> } >> >> enum B { >> case value(String) >> case c(C) >> } >> >> enum C { >> case value(Double) >> case a(A) >> } >> The rules state that—ignoring the trivial cases—"A derives P if B conforms >> to P," and "B derives P if Cconforms to P," and "C derives P if A conforms >> to P." The same observation about recursion and the finiteness of instances >> from above holds here, so we can generalize the rule above as follows: >> >> A type T can derive P if all members of T conform to P or are of types found >> in cycles that lead back to Tsuch that the members of those other types >> along the cycle also all conform to P or are themselves along such a cycle. >> >> >> <https://gist.github.com/allevato/2fd10290bfa84accfbe977d8ac07daad#other-considerations>Other >> considerations >> >> When conditional conformances are supported in Swift, generic types should >> conditionally derive Equatable and Hashable for type argument substitutions >> where the rules above are satisfied. >> >> A notable side effect of this is that Optional<Wrapped> would derive >> Equatable and Hashable conformance when Wrapped conforms to those protocols, >> because it is an enum that would satisfy the rules described above. Its >> implementation would not need to be in the standard library (but there is >> also nothing preventing it from being there). >> >> Conditional conformances will also significantly improve derivability >> coverage over other payload/member types. For example, consider a struct >> containing a stored property that is an array of Equatable types: >> >> struct Foo { >> var values: [String] >> } >> Today, Array<String> does not conform to Equatable, so its presence would >> prohibit Foo from deriving Equatable. However, once Swift can express the >> conformance Array<Element>: Equatable where Element: Equatable, Foo would >> automatically derive Equatable as well. This makes derived conformances >> significantly more powerful. >> >> >> <https://gist.github.com/allevato/2fd10290bfa84accfbe977d8ac07daad#implementation-details>Implementation >> details >> >> An enum T that derives Equatable will receive a compiler-generated >> implementation of static == (lhs: T, rhs: T) -> Bool that returns true if >> and only if lhs and rhs are the same case and have payloads that are >> memberwise-equal. >> >> An enum T that derives Hashable will receive a compiler-generated >> implementation of var hashValue: Int { get }that uses an unspecified hash >> function† to compute the hash value by incorporating the case's ordinal >> (i.e., definition order) followed by the hash values of its associated >> values as its terms, also in definition order. >> >> A struct T that derives Equatable will receive a compiler-generated >> implementation of static == (lhs: T, rhs: T) -> Bool that returns true if >> and only if lhs.x == rhs.x for all stored properties in T. >> >> A struct T that derives Hashable will receive a compiler-generated >> implementation of var hashValue: Int { get } that uses an unspecified hash >> function† to compute the hash value by incorporating the hash values of the >> fields as its terms, in definition order. >> >> > > _______________________________________________ > swift-evolution mailing list > [email protected] > https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
