[swift-evolution] Pitch: Automatically deriving Equatable/Hashable for more value types

Xiaodi Wu xiaodi.wu at gmail.com
Fri May 5 14:37:48 CDT 2017


It would have to be compiler magic. If I recall, the only factor preventing
it is that no one has implemented it, but one of the core team members has
noted that it should be fairly straightforward. I don't have the time right
now or I'd seriously take a stab at it.


On Fri, May 5, 2017 at 14:20 Tony Allevato <tony.allevato at gmail.com> wrote:

> On Fri, May 5, 2017 at 12:18 PM Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>
>> Hmm, with these complications, I regret suggesting that this could be
>> extended to classes so easily. Definitely some interesting solutions, but
>> my inclination as well would be to leave classes out entirely for the
>> moment and let it percolate for Swift 4.1+. After all, it's an additive
>> convenience.
>>
>> Any thought as to including tuples though?
>>
>
> Can non-nominal types conform to protocols? I thought that was the main
> limitation for supporting these conformances on tuples.
>
> I would love to support them if possible to get rid of the arbitrary ==
> implementations in the standard library and make multi-component dictionary
> keys easier to work with, but my understanding was that the above issue was
> the bigger factor preventing it.
>
>
>
>
>> On Fri, May 5, 2017 at 13:51 Matthew Johnson via swift-evolution <
>> swift-evolution at swift.org> wrote:
>>
>>> On May 5, 2017, at 1:33 PM, Tony Allevato <tony.allevato at gmail.com>
>>> wrote:
>>>
>>>
>>>
>>> On Fri, May 5, 2017 at 11:07 AM Matthew Johnson <matthew at anandabits.com>
>>> wrote:
>>>
>>>> On May 5, 2017, at 10:45 AM, Tony Allevato via swift-evolution <
>>>> swift-evolution at swift.org> wrote:
>>>>
>>>> Thanks for your feedback, everybody!
>>>>
>>>>
>>>> Thanks for continuing to drive this forward!
>>>>
>>>>
>>>> I've updated the gist
>>>> <https://gist.github.com/allevato/2fd10290bfa84accfbe977d8ac07daad> to
>>>> reflect what seems to be a consensus here:
>>>>
>>>> * Derived conformances are now opt-in (this makes the recursive case
>>>> *much* cleaner, and the complexity involved in that section has been
>>>> completely removed)
>>>>
>>>>
>>>> Can the opt-in conformance be declared in an extension?  If so, can the
>>>> extension be in a different module than the original declaration?  If so,
>>>> do you intend any restrictions, such as requiring all members of the type
>>>> declared in a different module to be public?  My initial thought is that
>>>> this should be possible as long as all members are visible.
>>>>
>>>
>>> Declaring the conformance in an extension in the same module should
>>> definitely be allowed; I believe this would currently be the only way to
>>> support conditional conformances (such as the `Optional: Hashable where
>>> Wrapped: Hashable` example in the updated draft), without requiring deeper
>>> syntactic changes.
>>>
>>> I'm less sure about conformances being added in other modules, but I'm
>>> inclined to agree with your assessment. I could see two ways of
>>> interpreting it:
>>>
>>> * E/H can only be derived in an extension in an external module if all
>>> the members are accessible (and the other conditions are met).
>>> * E/H can be derived in an extension in an external module using only
>>> the subset of accessible members (if the other conditions are met).
>>>
>>> These are subtly different. The argument for the first would be "if you
>>> want to add E/H to a type in a different module, you must *consciously*
>>> decide which members you want to use in those computations". The argument
>>> for the second would be "you can already make a type in a different module
>>> conform to E/H and you'd be restricted to the accessible members there, so
>>> let's make that path easier for users too."
>>>
>>> The first case is probably the safer choice. I'm not sure about the
>>> implementation difficulty of each.
>>>
>>>
>>>> * Classes are supported now as well
>>>>
>>>> Please take a look at the updated version and let me know if there are
>>>> any concerns! If folks like it, I'll prepare a pull request.
>>>>
>>>>
>>>> Will the synthesis for classes dispatch through a non-final method
>>>> which is expected to be overridden by subclasses?  You don’t explicitly
>>>> state this but it seems implied.  If so, what if  the subclass requires a
>>>> custom implementation?  This would require the signature of the non-final
>>>> method to be part of the synthesis contract.
>>>>
>>>
>>>> Supporting non-final classes introduces enough complexity (especially
>>>> when multiple modules are involved).  I would hate to see it get
>>>> sidetracked in discussions regarding non-final classes and miss the Swift 4
>>>> window because of that.  Given the limited time left for Swift 4 it might
>>>> be better to keep the initial proposal simpler and consider a followup in
>>>> the Swift 5 timeframe to build on the initial proposal.
>>>>
>>>
>>> For ==, the operator must already be "class final" or "static"
>>> regardless of this proposal, and it can't be "overridden" as such in
>>> subclasses because the arguments would be different (lhs and rhs would be
>>> the subclass, not the superclass). So the compiler should be able to
>>> generate the correct implementation for subclasses in all cases, right?
>>>
>>>
>>> This won’t work because Equatable has a `Self` requirement so the `==`
>>> defined by the initial conforming class would be called.  In order to
>>> support non-final classes you would need to have that dispatch through
>>> something like an `isEqual` method which *can* be overridden.
>>>
>>>
>>> For hashValue, I think the possibilities are:
>>>
>>> * Sub is a subclass of Super. Super conforms to Hashable and implements
>>> non-final hashValue. The compiler can derive it for Sub and call
>>> super.hashValue in its implementation.
>>>
>>>
>>> Yes, this makes sense.  The primary difficulty with Hashable is that it
>>> refines Equatable.  Refining a non-final implementation of `hashValue` is
>>> relatively straightforward.
>>>
>>> * Sub is a subclass of Super. Super conforms to Hashable and implements
>>> a final hashValue. The compiler cannot derive one for Super and would
>>> silently not do so.
>>>
>>>
>>> Do you mean “the compiler cannot derive one for Sub”?
>>>
>>> * Sub is a subclass of Super. Super does not conform to Hashable, but
>>> Sub asks to derive it. This can either (1) not be allowed, telling the user
>>> that they need to write it manually in this case, or (2) be allowed and use
>>> all accessible members to compute the hashValue (including those from the
>>> superclass).
>>>
>>> What do Encodable/Decodable do in these situations? It seems similar
>>> solutions there would apply here.
>>>
>>>
>>> That’s a good question.  I don’t recall whether this was addressed
>>> explicitly or not.
>>>
>>>
>>> But after writing this all out, I'm inclined to agree that I'd rather
>>> see structs/enums make it into Swift 4 even if it meant pushing classes to
>>> Swift 4+x.
>>>
>>>
>>> That is reasonable.
>>>
>>> On the other hand, I think you could come up with straightforward
>>> semantics for synthesizing conformance for final classes as well.  Final
>>> classes with no superclass should be straightforward.  Final classes that
>>> do have a superclass would be similarly straightforward if we decide to
>>> allow this as described in option (2) above regarding hashValue.
>>>
>>> I’m on the fence on this - if we can include final classes using option
>>> (2) without jeopardizing getting this in for Swift 4 I would support that.
>>> If it’s going to put support for value types in Swift 4 at risk then I
>>> would not.
>>>
>>>
>>>
>>>
>>>>
>>>>
>>>>
>>>> On Fri, May 5, 2017 at 8:16 AM Nevin Brackett-Rozinsky via
>>>> swift-evolution <swift-evolution at swift.org> wrote:
>>>>
>>>>> On Fri, May 5, 2017 at 1:47 AM, Xiaodi Wu via swift-evolution <
>>>>> swift-evolution at swift.org> wrote:
>>>>>
>>>>>> On Fri, May 5, 2017 at 12:41 AM, Brent Royal-Gordon <
>>>>>> brent at architechies.com> wrote:
>>>>>>
>>>>>>> I would think only final classes could participate in this, since a
>>>>>>> subclassable class would need to allow subclasses to override equality, and
>>>>>>> you can't override a static `==` operator method.
>>>>>>>
>>>>>>
>>>>>> I work so rarely with classes that I'm embarrassed to have to ask
>>>>>> this question: can classes not satisfy Equatable with a `public class func
>>>>>> ==`?
>>>>>>
>>>>>
>>>>> Currently:
>>>>>
>>>>> class C: Equatable {
>>>>>     class func == (lhs: C, rhs: C) -> Bool {
>>>>>         return lhs === rhs
>>>>>     }
>>>>> }
>>>>>
>>>>> Yields an error, “Operator '==' declared in non-final class 'C' must
>>>>> be 'final'”.
>>>>>
>>>>> Nevin
>>>>> _______________________________________________
>>>>> swift-evolution mailing list
>>>>> swift-evolution at swift.org
>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>>>
>>>> _______________________________________________
>>>> swift-evolution mailing list
>>>> swift-evolution at swift.org
>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>>
>>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org
>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170505/ec6967a9/attachment.html>


More information about the swift-evolution mailing list