[Accepted] SE-0185 - Synthesizing Equatable and Hashable conformance

We can override the protocol default implementation in the extension, but the issue I see with default implementation in Swift is that if I pass the object created this way around in a type erased container (Any : Protocol1 like it was common for many to pass id<Protocol> around in the Objective-C days, a good practice IMHO) then my overrode would not be called, but the default implementation will be used instead. I would be far more comfortable with this “magic” provided for free of default implementations were dynamically dispatched.

···

Sent from my iPhone

On 19 Aug 2017, at 19:06, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

On Sat, Aug 19, 2017 at 06:07 Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

On 19 Aug 2017, at 11:44, Tino Heth <2th@gmx.de> wrote:
Am 17.08.2017 um 20:11 schrieb Haravikk via swift-evolution <swift-evolution@swift.org>:
For me the whole point of a basic protocol is that it forces me to implement some requirements in order to conform; I can throw a bunch of protocols onto a type and know that it won't compile until I've finished it, developers get distracted, leave things unfinished to go back to later, make typos etc. etc. To me declaring a conformance is a declaration of "my type will meet the requirements for this make, sure I do it", not "please, please use some magic to do this for me"; there needs to be a clear difference between the two.

My conclusion isn't as pessimistic as yours, but I share your objections: Mixing a normal feature (protocols) with compiler magic doesn't feel right to me — wether it's Equatable, Hashable, Codable or Error.
It's two different concepts with a shared name*, so I think even AutoEquatable wouldn't be the right solution, and something like equatable would be a much better indicator for what is happening.

Besides that specific concern, I can't fight the feeling that the evolution process doesn't work well for proposals like this:
It's a feature that many people just want to have as soon as possible, and concerns regarding the long-term effects are more or less washed away with eagerness.

- Tino

* for the same reason, I have big concerns whenever someone proposes to blur the line between tuples and arrays

Agreed. To be clear though; in spite of my pessimism this is a feature that I do want, but I would rather not have it at all than have it implemented in a way that hides bugs and sets a horrible precedent for the future.

This was already touched upon during review, but to reiterate, the analogy to default protocol implementations is meant specifically to address this point about "hiding bugs." Yes, this feature cannot currently be implemented as a default protocol implementation without magic; with better reflection facilities there's a good chance that one day it might be, but that's not the reason why it's being compared to default protocol implementations. The reason for the comparison is that this feature only "hides bugs" like a default protocol implementation "hides bugs" (in the I-conformed-my-type-and-forgot-to-override-the-default-and-the-compiler-won't-remind-me-anymore sense of "hiding bugs"), and the addition of default protocol implementations, unless I'm mistaken, isn't even considered an API change that requires Swift Evolution review.

Given Swift's emphasis on progressive disclosure, I'm fairly confident that once reflection facilities and/or code-generation facilities improve, many boilerplate-y protocol requirements will be given default implementations where they cannot be written today. With every advance in expressiveness, more protocol requirements that cannot currently have a default implementation will naturally acquire them. Since the degree to which the compiler will cease to give errors about non-implementation is directly in proportion to the boilerplate reduced, it's not a defect but a feature that these compiler errors go away. At the moment, it is a great idea to enable some of these improvements for specific common use cases before the general facilites for reflection and/or code-generation are improved in later versions of Swift, since the user experience would be expected to remain the same once those full facilities arrive.

I realise I may seem to be overreacting, but I really do feel that strongly about what I fully believe is a mistake. I understand people's enthusiasm for the feature, I do; I hate boilerplate as much as the next developer, but as you say, it's not a reason to rush forward, especially when this is not something that can be easily changed later.

That's a big part of the problem; the decisions here are not just about trimming boilerplate for Equatable/Hashable, it's also about the potential overreach of every synthesised feature now and in the future as well.
_______________________________________________
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

I would rather no code at all use the implicit version; one of my points is that it's not something that's easily changed after the fact, which is why it needs to be done correctly now.

I'm open to any method that makes opting in to the synthesised conformance explicit; I still think a specifically named protocol is the simplest, but I'm not married to that as a solution; attributes, keywords etc. are all fine too, whatever is the easiest way to opt-in to the behaviour explicitly without ambiguity. I'm not 100% sure exactly what you mean by "add a command within the definition block", or is an attribute/keyword what you meant?

···

On 19 Aug 2017, at 19:46, Daryle Walker <darylew@mac.com> wrote:

On Aug 19, 2017, at 7:06 AM, Haravikk via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On 19 Aug 2017, at 11:44, Tino Heth <2th@gmx.de <mailto:2th@gmx.de>> wrote:

Am 17.08.2017 um 20:11 schrieb Haravikk via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:
For me the whole point of a basic protocol is that it forces me to implement some requirements in order to conform; I can throw a bunch of protocols onto a type and know that it won't compile until I've finished it, developers get distracted, leave things unfinished to go back to later, make typos etc. etc. To me declaring a conformance is a declaration of "my type will meet the requirements for this make, sure I do it", not "please, please use some magic to do this for me"; there needs to be a clear difference between the two.

My conclusion isn't as pessimistic as yours, but I share your objections: Mixing a normal feature (protocols) with compiler magic doesn't feel right to me — wether it's Equatable, Hashable, Codable or Error.
It's two different concepts with a shared name*, so I think even AutoEquatable wouldn't be the right solution, and something like equatable would be a much better indicator for what is happening.

Besides that specific concern, I can't fight the feeling that the evolution process doesn't work well for proposals like this:
It's a feature that many people just want to have as soon as possible, and concerns regarding the long-term effects are more or less washed away with eagerness.

- Tino

* for the same reason, I have big concerns whenever someone proposes to blur the line between tuples and arrays

Agreed. To be clear though; in spite of my pessimism this is a feature that I do want, but I would rather not have it at all than have it implemented in a way that hides bugs and sets a horrible precedent for the future.

I tried to make a split thread for this, but would you object to synthesized conformance if we had to explicitly add a command within the definition block to trigger the synthesis? If we add strong type-aliases, we could reuse the directive to copy an interface (method, inner type, property, or conformed-to protocol) from the underlying type to the current type for synthesis too. The only problem would be backward compatibility; once added, we would urge users to explicitly list “publish Equatable” for synthesis, but what about code that already uses the implicit version (since this feature will probably be released for at least one Swift version by the time strong type-aliases happen), do we force users to change their code?

We can override the protocol default implementation in the extension, but
the issue I see with default implementation in Swift is that if I pass the
object created this way around in a type erased container (Any : Protocol1
  like it was common for many to pass id<Protocol> around in the
Objective-C days, a good practice IMHO) then my overrode would not be
called, but the default implementation will be used instead. I would be far
more comfortable with this “magic” provided for free of default
implementations were dynamically dispatched.

Are you referring to protocol extension methods? Those are not default
implementations, do not have a corresponding protocol requirement that can
be overridden, and are not what's being discussed here.

···

On Sat, Aug 19, 2017 at 1:13 PM, Goffredo Marocchi <panajev@gmail.com> wrote:

Sent from my iPhone

On 19 Aug 2017, at 19:06, Xiaodi Wu via swift-evolution < > swift-evolution@swift.org> wrote:

On Sat, Aug 19, 2017 at 06:07 Haravikk via swift-evolution < > swift-evolution@swift.org> wrote:

On 19 Aug 2017, at 11:44, Tino Heth <2th@gmx.de> wrote:

Am 17.08.2017 um 20:11 schrieb Haravikk via swift-evolution < >> swift-evolution@swift.org>:

For me the whole point of a basic protocol is that it forces me to
implement some requirements in order to conform; I can throw a bunch of
protocols onto a type and know that it won't compile until I've finished
it, developers get distracted, leave things unfinished to go back to later,
make typos etc. etc. To me declaring a conformance is a declaration of "my
type will meet the requirements for this make, sure I do it", not "please,
please use some magic to do this for me"; there needs to be a clear
difference between the two.

My conclusion isn't as pessimistic as yours, but I share your objections:
Mixing a normal feature (protocols) with compiler magic doesn't feel right
to me — wether it's Equatable, Hashable, Codable or Error.
It's two different concepts with a shared name*, so I think even
AutoEquatable wouldn't be the right solution, and something like equatable
would be a much better indicator for what is happening.

Besides that specific concern, I can't fight the feeling that the
evolution process doesn't work well for proposals like this:
It's a feature that many people just want to have as soon as possible,
and concerns regarding the long-term effects are more or less washed away
with eagerness.

- Tino

* for the same reason, I have big concerns whenever someone proposes to
blur the line between tuples and arrays

Agreed. To be clear though; in spite of my pessimism this *is* a feature
that I *do* want, but I would rather not have it at all than have it
implemented in a way that hides bugs and sets a horrible precedent for the
future.

This was already touched upon during review, but to reiterate, the analogy
to default protocol implementations is meant specifically to address this
point about "hiding bugs." Yes, this feature cannot currently be
implemented as a default protocol implementation without magic; with better
reflection facilities there's a good chance that one day it might be, but
that's not the reason why it's being compared to default protocol
implementations. The reason for the comparison is that this feature only
"hides bugs" like a default protocol implementation "hides bugs" (in the
I-conformed-my-type-and-forgot-to-override-the-
default-and-the-compiler-won't-remind-me-anymore sense of "hiding bugs"),
and the addition of default protocol implementations, unless I'm mistaken,
isn't even considered an API change that requires Swift Evolution review.

Given Swift's emphasis on progressive disclosure, I'm fairly confident
that once reflection facilities and/or code-generation facilities improve,
many boilerplate-y protocol requirements will be given default
implementations where they cannot be written today. With every advance in
expressiveness, more protocol requirements that cannot currently have a
default implementation will naturally acquire them. Since the degree to
which the compiler will cease to give errors about non-implementation is
directly in proportion to the boilerplate reduced, it's not a defect but a
feature that these compiler errors go away. At the moment, it is a great
idea to enable some of these improvements for specific common use cases
before the general facilites for reflection and/or code-generation are
improved in later versions of Swift, since the user experience would be
expected to remain the same once those full facilities arrive.

I realise I may seem to be overreacting, but I really do feel that

strongly about what I fully believe is a mistake. I understand people's
enthusiasm for the feature, I do; I hate boilerplate as much as the next
developer, but as you say, it's not a reason to rush forward, especially
when this is not something that can be easily changed later.

That's a big part of the problem; the decisions here are not just about
trimming boilerplate for Equatable/Hashable, it's also about the potential
overreach of every synthesised feature now and in the future as well.
_______________________________________________
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

Sorry, I thought that the default implementation in the protocol extension was how this was provided.

Providing Default Implementations
You can use protocol extensions to provide a default implementation to any method or computed property requirement of that protocol

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-ID521

···

Sent from my iPhone

On 19 Aug 2017, at 19:28, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Sat, Aug 19, 2017 at 1:13 PM, Goffredo Marocchi <panajev@gmail.com> wrote:
We can override the protocol default implementation in the extension, but the issue I see with default implementation in Swift is that if I pass the object created this way around in a type erased container (Any : Protocol1 like it was common for many to pass id<Protocol> around in the Objective-C days, a good practice IMHO) then my overrode would not be called, but the default implementation will be used instead. I would be far more comfortable with this “magic” provided for free of default implementations were dynamically dispatched.

Are you referring to protocol extension methods? Those are not default implementations, do not have a corresponding protocol requirement that can be overridden, and are not what's being discussed here.

Sent from my iPhone

On 19 Aug 2017, at 19:06, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

On Sat, Aug 19, 2017 at 06:07 Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

On 19 Aug 2017, at 11:44, Tino Heth <2th@gmx.de> wrote:
Am 17.08.2017 um 20:11 schrieb Haravikk via swift-evolution <swift-evolution@swift.org>:
For me the whole point of a basic protocol is that it forces me to implement some requirements in order to conform; I can throw a bunch of protocols onto a type and know that it won't compile until I've finished it, developers get distracted, leave things unfinished to go back to later, make typos etc. etc. To me declaring a conformance is a declaration of "my type will meet the requirements for this make, sure I do it", not "please, please use some magic to do this for me"; there needs to be a clear difference between the two.

My conclusion isn't as pessimistic as yours, but I share your objections: Mixing a normal feature (protocols) with compiler magic doesn't feel right to me — wether it's Equatable, Hashable, Codable or Error.
It's two different concepts with a shared name*, so I think even AutoEquatable wouldn't be the right solution, and something like equatable would be a much better indicator for what is happening.

Besides that specific concern, I can't fight the feeling that the evolution process doesn't work well for proposals like this:
It's a feature that many people just want to have as soon as possible, and concerns regarding the long-term effects are more or less washed away with eagerness.

- Tino

* for the same reason, I have big concerns whenever someone proposes to blur the line between tuples and arrays

Agreed. To be clear though; in spite of my pessimism this is a feature that I do want, but I would rather not have it at all than have it implemented in a way that hides bugs and sets a horrible precedent for the future.

This was already touched upon during review, but to reiterate, the analogy to default protocol implementations is meant specifically to address this point about "hiding bugs." Yes, this feature cannot currently be implemented as a default protocol implementation without magic; with better reflection facilities there's a good chance that one day it might be, but that's not the reason why it's being compared to default protocol implementations. The reason for the comparison is that this feature only "hides bugs" like a default protocol implementation "hides bugs" (in the I-conformed-my-type-and-forgot-to-override-the-default-and-the-compiler-won't-remind-me-anymore sense of "hiding bugs"), and the addition of default protocol implementations, unless I'm mistaken, isn't even considered an API change that requires Swift Evolution review.

Given Swift's emphasis on progressive disclosure, I'm fairly confident that once reflection facilities and/or code-generation facilities improve, many boilerplate-y protocol requirements will be given default implementations where they cannot be written today. With every advance in expressiveness, more protocol requirements that cannot currently have a default implementation will naturally acquire them. Since the degree to which the compiler will cease to give errors about non-implementation is directly in proportion to the boilerplate reduced, it's not a defect but a feature that these compiler errors go away. At the moment, it is a great idea to enable some of these improvements for specific common use cases before the general facilites for reflection and/or code-generation are improved in later versions of Swift, since the user experience would be expected to remain the same once those full facilities arrive.

I realise I may seem to be overreacting, but I really do feel that strongly about what I fully believe is a mistake. I understand people's enthusiasm for the feature, I do; I hate boilerplate as much as the next developer, but as you say, it's not a reason to rush forward, especially when this is not something that can be easily changed later.

That's a big part of the problem; the decisions here are not just about trimming boilerplate for Equatable/Hashable, it's also about the potential overreach of every synthesised feature now and in the future as well.
_______________________________________________
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

The syntax to copy an interface from an underlying type to one of its strong type-aliases is a new directive within the definition block:

alter MyInt16: Int16, Hashable {
publish Equatable // Automatically copies definitions from Int16 needed to conform to Equatable
var hashValue: Int { /*…*/ } // A protocol can be completed with a mix of published and direct definitions
}

Since we would be introducing an explicit way to declare implementation of a conformance (the “publish” directive), we could reuse the directive for Equatable/Hashable/Encodable/Decodable definitions in non-strong-aliases and make the current implicit definitions obsolete. The problem then would be backwards compatibility; could we force users to go from implicit to explicit synthesized conformance?

The original point of publishing is to selectively control which parts of the underlying type’s interface get copied. Automatically synthesized conformance, if it stays after strong type-aliases are introduced, would screw with that (unless synthesized conformance is ignored for strong type-aliases; i.e. our conformance exception gets a counter exception).

···

On Aug 19, 2017, at 3:29 PM, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

On 19 Aug 2017, at 19:46, Daryle Walker <darylew@mac.com <mailto:darylew@mac.com>> wrote:

On Aug 19, 2017, at 7:06 AM, Haravikk via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Agreed. To be clear though; in spite of my pessimism this is a feature that I do want, but I would rather not have it at all than have it implemented in a way that hides bugs and sets a horrible precedent for the future.

I tried to make a split thread for this, but would you object to synthesized conformance if we had to explicitly add a command within the definition block to trigger the synthesis? If we add strong type-aliases, we could reuse the directive to copy an interface (method, inner type, property, or conformed-to protocol) from the underlying type to the current type for synthesis too. The only problem would be backward compatibility; once added, we would urge users to explicitly list “publish Equatable” for synthesis, but what about code that already uses the implicit version (since this feature will probably be released for at least one Swift version by the time strong type-aliases happen), do we force users to change their code?

I would rather no code at all use the implicit version; one of my points is that it's not something that's easily changed after the fact, which is why it needs to be done correctly now.

I'm open to any method that makes opting in to the synthesised conformance explicit; I still think a specifically named protocol is the simplest, but I'm not married to that as a solution; attributes, keywords etc. are all fine too, whatever is the easiest way to opt-in to the behaviour explicitly without ambiguity. I'm not 100% sure exactly what you mean by "add a command within the definition block", or is an attribute/keyword what you meant?


Daryle Walker
Mac, Internet, and Video Game Junkie
darylew AT mac DOT com

There are default implementations and extension methods. Both are written
inside protocol extensions. Default implementations are dynamically
dispatched, but extension methods are not. A default implementation
implements a protocol requirement. An extension method adds a method to a
protocol which is not a requirement.

···

On Sat, Aug 19, 2017 at 3:26 PM, Goffredo Marocchi <panajev@gmail.com> wrote:

Sorry, I thought that the default implementation in the protocol extension
was how this was provided.

Providing Default Implementations

You can use protocol extensions to provide a default implementation to any
method or computed property requirement of that protocol

https://developer.apple.com/library/content/documentation/
Swift/Conceptual/Swift_Programming_Language/Protocols.html#//apple_ref/
doc/uid/TP40014097-CH25-ID521