> On May 8, 2017, at 3:17 PM, Michael Ilseman via swift-evolution > <swift-evolution@swift.org> wrote: > > 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?
How does Codable handle the equivalent question? > >> On May 4, 2017, at 3:20 PM, Andrew Bennett via swift-evolution >> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> 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 <xiaodi...@gmail.com >> <mailto:xiaodi...@gmail.com>> 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 <rjmcc...@apple.com >> <mailto:rjmcc...@apple.com>> wrote: >>> On May 4, 2017, at 6:10 PM, Andrew Bennett via swift-evolution >>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> 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 >>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> 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 >>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> 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 >> 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
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution