Hello Haravikk,

I'lm worried that you fail at preventing a real problem. May I suggest a change 
in your strategy?

Sometimes, sample code greatly helps turning subtle ideas into blatant 
evidence. After all, subtleties are all about corner cases, and corner cases 
are the blind spots of imagination. What about giving that little something 
that would help your readers grasp your arguments?

I don't quite know what example you will provide, but I could suggest the 
exhibition of a practical problem with Equatable synthesis. We'll know better 
if the problem can arise in the Standard lib, in third-party libraries, at 
application level, or at several scales at the same time. It would also be nice 
to see your solution to the problem, that is to say an alternative that still 
provides code synthesis for developers that want to opt in the feature, but 
avoids the caveat of the initial example. I hope this would greatly help the 
discussion move forward.

Last general comment about the topic: if Haravikk is right, and that code 
synthesis should indeed be explicit, then that wouldn't be such a shame.

My two cents,
Gwendal Roué


> Le 9 sept. 2017 à 13:41, Haravikk via swift-evolution 
> <swift-evolution@swift.org> a écrit :
> 
>> 
>> On 9 Sep 2017, at 09:33, Xiaodi Wu <xiaodi...@gmail.com 
>> <mailto:xiaodi...@gmail.com>> wrote:
>> 
>> 
>> On Sat, Sep 9, 2017 at 02:47 Haravikk via swift-evolution 
>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>> 
>>> On 9 Sep 2017, at 02:02, Xiaodi Wu <xiaodi...@gmail.com 
>>> <mailto:xiaodi...@gmail.com>> wrote:
>>> 
>>> On Fri, Sep 8, 2017 at 4:00 PM, Itai Ferber via swift-evolution 
>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>> 
>>> 
>>>> On Sep 8, 2017, at 12:46 AM, Haravikk via swift-evolution 
>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>>> 
>>>> 
>>>>> On 7 Sep 2017, at 22:02, Itai Ferber <ifer...@apple.com 
>>>>> <mailto:ifer...@apple.com>> 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.
>> 
>> You are entirely against Equatable having a default implementation for ==. 
>> This is unequivocally stated. Others favor such a default implementation and 
>> feel that in the absence of a way to spell this in Swift itself, it should 
>> be magic for the time being. For the purposes of this argument it really is 
>> not pertinent that you are not also against something else; you're asking us 
>> to discuss why you are against a particular thing that others are for.
> 
> FFS, how much clearer can I make this? I AM NOT AGAINST THE FEATURE.
> 
> What I am against is the way in which it is being provided implicitly rather 
> than explicitly, in particular as a retroactive change to existing protocols 
> in a way that introduces potential for bugs that are currently impossible, 
> but also in general.
> 
>> As repeatedly answered by others, nothing here is specific to synthesized 
>> default implementations, as more powerful reflection will gradually allow 
>> them to be non-synthesised.
> 
> And as repeatedly stated by me; I am not treating synthesised vs. run-time 
> reflection any differently, I specifically included both in the original 
> proposal.
> 
>> As pointed out very cogently by Itai, you assert but offer no evidence, 
>> either in principle or empirically, that going too far by reflection is 
>> worse than going not far enough without reflection in terms of likelihood of 
>> a default implementation being inappropriate for conforming types.
> 
> As I have also repeatedly pointed out it is not an issue of "not going far 
> enough" vs. "going too far"; if a default implementation lacks information 
> then it should not be provided, doing so regardless is a flaw in the protocol 
> design and not something that this proposal attempts to address (as such a 
> thing is likely impossible).
> 
> Reflective implementations necessarily go too far, because they literally 
> know nothing about the concrete type with any certainty, except for the 
> properties that are defined in the protocol (which do not require reflection 
> or synthesis in the first place).
> 
> And precisely what kind of "evidence" am I expected to give? This is a set of 
> features that do not exist yet, I am trying to argue in favour of an explicit 
> end-developer centric opt-in rather than an implicit protocol designer 
> centric one. Yet no-one seems interested in the merits of allowing developers 
> to choose what they want, rather than having implicit behaviours appear 
> potentially unexpectedly.
> 
>> Therefore, your argument reduces to one about which default implementations 
>> generally ought or ought not to be provided--that is, that they ought to be 
>> provided only when their correctness can be guaranteed for all (rather than 
>> almost all) possible conforming types. To which point I sketched a rebuttal 
>> above.
> 
> If a protocol defines something, and creates a default implementation based 
> only upon those definitions then it must by its very nature be correct. A 
> concrete type may later decided to go further, but that is a feature of the 
> concrete type, not a failure of the protocol itself which can function 
> correctly within the context it created. You want to talk evidence, yet there 
> has been no example given that proves otherwise; thus far only Itai has 
> attempted to do so, but I have already pointed out the flaws with that 
> example.
> 
> The simple fact is that a default implementation may either be flawed or not 
> within the context of the protocol itself; but a reflective or synthetic 
> implementation by its very nature goes beyond what the protocol defines and 
> so is automatically flawed because as it does not rely on the end-developer 
> to confirm correctness, not when provided implicitly at least.
> 
>> 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?).
>> 
>> This has emphatically not gone ignored, as I have myself responded to this 
>> point in an earlier thread in which you commented, as well as many others. 
>> Crucially, no existing conforming type changes its behavior, as they have 
>> all had to implement these requirements themselves. And as I said to you 
>> already, the addition of a synthesized default implementation no more "hides 
>> bugs" going forward than the addition of a non-synthesized default 
>> implementation to an existing protocol, and we do that with some frequency 
>> without even Swift Evolution review.
> 
> Feel free to a supply a non-synthesised default implementation for Equatable 
> without the use of reflection. Go-on, I'll wait.
> You insist on suggesting these are the same thing, yet if you can't provide 
> one then clearly they are not.
> 
>> Put another way, what the proposal about synthesizing implementations for 
>> Equatable and Hashable was about can be thought of in two parts: (a) should 
>> there be default implementations; and (b) given that it is impossible to 
>> write these in Swift, should we use magic? Now, as I said above, adding 
>> default implementations isn't (afaik) even considered an API change that 
>> requires review on this list. Really, what people were debating was (b), 
>> whether it is worth it to implement compiler-supported magic to make these 
>> possible. Your disagreement has to do with (a) and not (b).
> 
> Wrong. The use of magic in this case produces something else entirely; that's 
> the whole point. It is not the same, otherwise it wouldn't be needed at all. 
> It doesn't matter if it's compiler magic, some external script or a native 
> macro, ultimately they are all doing something with a concrete type that is 
> currently not possible.
> 
> And once again; I am not arguing against a default implementation that cuts 
> boilerplate, I am arguing against it being implicit. What I want is to be the 
> one asking for it, because it is not reasonable to assume that just throwing 
> it in there is always going to be fine, because it quite simply is not.
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution 
> <https://lists.swift.org/mailman/listinfo/swift-evolution>
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to