> On 9 Sep 2017, at 02:02, Xiaodi Wu <[email protected]> wrote:
>
> On Fri, Sep 8, 2017 at 4:00 PM, Itai Ferber via swift-evolution
> <[email protected] <mailto:[email protected]>> wrote:
>
>
>> On Sep 8, 2017, at 12:46 AM, Haravikk via swift-evolution
>> <[email protected] <mailto:[email protected]>> wrote:
>>
>>
>>> On 7 Sep 2017, at 22:02, Itai Ferber <[email protected]
>>> <mailto:[email protected]>> wrote:
>>>
>>> protocol Fooable : Equatable { // Equatable is just a simple example
>>> var myFoo: Int { get }
>>> }
>>>
>>> extension Fooable {
>>> static func ==(_ lhs: Self, _ rhs: Self) -> Bool {
>>> return lhs.myFoo == rhs.myFoo
>>> }
>>> }
>>>
>>> struct X : Fooable {
>>> let myFoo: Int
>>> let myName: String
>>> // Whoops, forgot to give an implementation of ==
>>> }
>>>
>>> print(X(myFoo: 42, myName: "Alice") == X(myFoo: 42, myName: "Bob")) // true
>>> This property is necessary, but not sufficient to provide a correct
>>> implementation. A default implementation might be able to assume something
>>> about the types that it defines, but it does not necessarily know enough.
>>
>> Sorry but that's a bit of a contrived example; in this case the protocol
>> should not implement the equality operator if more information may be
>> required to define equality. It should only be implemented if the protocol
>> is absolutely clear that .myFoo is the only part of a Fooable that can or
>> should be compared as equatable, e.g- if a Fooable is a database record and
>> .myFoo is a primary key, the data could differ but it would still be a
>> reference to the same record.
>>
>> To be clear, I'm not arguing that someone can't create a regular default
>> implementation that also makes flawed assumptions, but that
>> synthesised/reflective implementations by their very nature have to, as they
>> cannot under every circumstance guarantee correctness when using parts of a
>> concrete type that they know nothing about.
>
> You can’t argue this both ways:
> If you’re arguing this on principle, that in order for synthesized
> implementations to be correct, they must be able to — under every
> circumstance — guarantee correctness, then you have to apply the same
> reasoning to default protocol implementations. Given a default protocol
> implementation, it is possible to come up with a (no matter how contrived)
> case where the default implementation is wrong. Since you’re arguing this on
> principle, you cannot reject contrived examples.
> If you are arguing this in practice, then you’re going to have to back up
> your argument with evidence that synthesized examples are more often wrong
> than default implementations. You can’t declare that synthesized
> implementations are by nature incorrect but allow default implementations to
> slide because in practice, many implementations are allowable. There’s a
> reason why synthesis passed code review and was accepted: in the majority of
> cases, synthesis was deemed to be beneficial, and would provide correct
> behavior. If you are willing to say that yes, sometimes default
> implementations are wrong but overall they’re correct, you’re going to have
> to provide hard evidence to back up the opposite case for synthesized
> implementations. You stated in a previous email that "A
> synthesised/reflective implementation however may return a result that is
> simply incorrect, because it is based on assumptions made by the protocol
> developer, with no input from the developer of the concrete type. In this
> case the developer must override it in to provide correct behaviour." — if
> you can back this up with evidence (say, taking a survey of a large number of
> model types and see if in the majority of cases synthesized implementation
> would be incorrect) to provide a compelling argument, then this is something
> that we should in that case reconsider.
>
> Well put, and I agree with this position 100%. However, to play devil's
> advocate here, let me summarize what I think Haravikk is saying:
>
> I think the "synthesized" part of this is a red herring, if I understand
> Haravikk's argument correctly. Instead, it is this:
>
> (1) In principle, it is possible to have a default implementation for a
> protocol requirement that produces the correct result--though not necessarily
> in the most performant way--for all possible conforming types, where by
> conforming we mean that the type respects both the syntactic requirements
> (enforced by the compiler) and the semantic requirements (which may not
> necessarily be enforceable by the compiler) of the protocol in question.
>
> (2) However, there exist *some* requirements that, by their very nature,
> cannot have default implementations which are guaranteed to produce the
> correct result for all conforming types. In Haravikk's view, no default
> implementations should be provided in these cases. (I don't necessarily
> subscribe to this view in absolute terms, but for the sake of argument let's
> grant this premise.)
>
> (3) Equatable, Hashable, and Codable requirements are, by their very nature,
> such requirements that cannot have default implementations guaranteed to be
> correct for all conforming types. Therefore, they should not have a default
> implementation. It just so happens that a default implementation cannot
> currently be written in Swift itself and must be synthesized, but Haravikk's
> point is that even if they could be written in native Swift through a
> hypothetical reflection facility, they should not be, just as many other
> protocol requirements currently could have default implementations written in
> Swift but should not have them because they cannot be guaranteed to produce
> the correct result.
>
> My response to this line of argumentation is as follows:
>
> For any open protocol (i.e., a protocol for which the universe of possible
> conforming types cannot be enumerated a priori by the protocol designer)
> worthy of being a protocol by the Swift standard ("what useful thing can you
> do with such a protocol that you could not without?"), any sufficiently
> interesting requirement (i.e., one for which user ergonomics would measurably
> benefit from a default implementation) either cannot have a universally
> guaranteed correct implementation or has an implementation which is also
> going to be the most performant one (which can therefore be a non-overridable
> protocol extension method rather than an overridable protocol requirement
> with a default implementation).
You're close, but still missing key points:
I am not arguing that features like these should not be provided, but that they
should not be provided implicitly, and that the developer should actually be
allowed to request them. That is exactly what this proposal is about, yet no
matter what I say everyone seems to be treating me like I'm against these
features entirely; I am not.
A non-reflective default implementation can only operate on the basis of what
the protocol itself defines; this means that it can always be correct within
the context of only what the protocol declares. At worst, a "pure" default
implementation can only be overly cautious, but strictly speaking this does not
make its behaviour incorrect, though, if it is a likely case it suggests that a
default implementation is not a good idea (as it is better to ensure the
developer provides any further information).
Synthetic/reflective implementations by their very nature must always make
assumptions that are tenuous at best, especially in cases of examining unknown
properties defined for an unknown purpose by an unknown type.
Another way of thinking about the difference is that a non-reflective default
implementation can only omit information that a developer must provide, but
this is an easily solved case as that's precisely what unimplemented protocol
requirements are for (to ask for more information/specific implementation
details).
With reflective behaviour however the default implementation can (and will) go
too far in what information it uses; while it might work fine some of the time,
this is a fundamental risk, and there is no mechanism through which a protocol
may force the developer to alter that behaviour. This makes the behaviour
insidious in nature, which is why it is better that a developer specifically
opt into it as it keeps the end developer in control, especially since these
behaviours are predicated on the idea that they are supposed to be a
convenience, but when the protocol is fucking around with properties it knows
nothing about then only the end developer can determine if that is the case.
And all of this continues to be a side-issue to the fact that in the specific
case of Equatable/Hashable, which thus far has gone ignored, is that bolting
this on retroactively to an existing protocol hides bugs. The issue of
reflective default implementations is less of a concern on very clearly and
well defined new protocols, though I still prefer more, rather than less,
control, but in the specific case of existing protocols this fucking about with
behaviours is reckless and foolish in the extreme, yet no-one on the core teams
seems willing or able to justify it, which only opens much wider concerns (how
am I to have any faith in Swift's development if the core team can't or won't
justify the creation of new bugs?).
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution