[swift-evolution] [Proposal] Explicit Synthetic Behaviour
Vladimir.S
svabox at gmail.com
Wed Sep 13 13:47:42 CDT 2017
On 13.09.2017 20:48, Tony Allevato wrote:
> On Wed, Sep 13, 2017 at 10:21 AM Vladimir.S via swift-evolution
> <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>
> On 13.09.2017 19:08, Ondrej Barina via swift-evolution wrote:
> > Maybe something like this as middle ground.
> >
> > protocol Equatable {
> > @syntetic static func ==(_ lhs: Self, _ rhs: Self) -> Bool
> > }
> >
> > protocol itself contains default implementation, but without real body.
> Instead the
> > function is marked that the real body is generated by compiler.
> > There is explicit mentions of default impl (by compiler magic), but it does not
> > affects users as they would still use protocol in normal way:
> >
> > struct Foo: Equatable { .... }
>
> Yes, I also thought about this. And personally for me it is also good solution, while
> `struct S: Equatable {/*nothing*/}` will *still* lead to compiler's error or at least
> warning about not implemented requirements.
> So, I'll be explicit regarding my intention: do I want requirements to be
> auto-generated or I want to do this manually.
>
> But still. If you see
>
> struct S: Equatable, Codable {
> // a lot of lines
> }
>
> you can't say right now if requirements for Equatable and/or Codable was implemented
> manually or will be auto-generated without checking all the code of a type. This
> knowledge can help to faster solve issues related to comparison/archiving.
> So for me the best solution is still 'deriving'-like keyword, which adds clarity and
> show intention without any boilerplate code:
>
>
> The sentences above apply equally to non-synthesized default protocol implementations:
>
> struct S: Foo {
> // a lot of lines
> }
>
> I can't say if the requirements for Foo were implemented manually by S or by a
> default implementation in Foo (which could be in a different module that I don't have
> source access to) without checking all the code for S. So this can't be used as a
> basis to rationalize special-casing synthesized implementations.
As was noted in this thread, some people believe that protocol synthesizing its
requirements by accessing type's fields is of a different kind than 'usual' protocol
with default implementation.
I belong to that camp. So, from my point of view, it is important to have
'deriving'-like marker for 'auto-senthesizeable' protocols as described above.
Also, some 'usual' protocol Foo can have no default implementations at the moment of
*writing* the code, but can have them at the moment of *compilation* via protocol
extension in separate file in project. So it is not possible to require similar
marker for such protocol.
But Equatable/Hashable/Codable protocols has auto-generation feature already at the
moment of writing the code and we can request that marker.
Vladimir.
>
>
> struct S: Equatable, deriving Codable {
> // all clear:
> // manually implemented Equatable
> // auto-generated Codable
>
> // a lot of lines
> }
>
> Vladimir.
>
> >
> > Ondrej B.
> >
> > On Wed, Sep 13, 2017 at 4:14 PM, Haravikk via swift-evolution
> > <swift-evolution at swift.org <mailto:swift-evolution at swift.org>
> <mailto:swift-evolution at swift.org <mailto:swift-evolution at swift.org>>> wrote:
> >
> >
> >> On 13 Sep 2017, at 03:26, Xiaodi Wu <xiaodi.wu at gmail.com
> <mailto:xiaodi.wu at gmail.com>
> >> <mailto:xiaodi.wu at gmail.com <mailto:xiaodi.wu at gmail.com>>> wrote:
> >>
> >> On Tue, Sep 12, 2017 at 11:43 AM, Haravikk via
> >> swift-evolution<swift-evolution at swift.org
> <mailto:swift-evolution at swift.org> <mailto:swift-evolution at swift.org
> <mailto:swift-evolution at swift.org>>>wrote:
> >>
> >>
> >>> On 12 Sep 2017, at 12:08, Xiaodi Wu <xiaodi.wu at gmail.com
> <mailto:xiaodi.wu at gmail.com>
> >>> <mailto:xiaodi.wu at gmail.com <mailto:xiaodi.wu at gmail.com>>> wrote:
> >>>
> >>>> On Mon, Sep 11, 2017 at 06:03 Haravikk via swift-evolution
> >>>> <swift-evolution at swift.org <mailto:swift-evolution at swift.org>
> <mailto:swift-evolution at swift.org <mailto:swift-evolution at swift.org>>> wrote:
> >>>>
> >>>> See, this is another flawed assumption; you are assuming that
> >>>> omitting a custom implementation of == is always intentional rather
> >>>> than an oversight, which is not guaranteed. This is one of my
> gripes
> >>>> with the retroactive change to Equatable, as it is
> >>>> currently*impossible* to omit an implementation.
> >>>
> >>>
> >>> Again, this applies equally to the addition of _any_ default
> >>> implementation. And again, such changes don’t even require Swift
> Evolution
> >>> approval.
> >>
> >> So what? Because the Swift Evolution process is currently deficient we
> >> should just give up on discussing problems with features and the language
> >> altogether?
> >>
> >>
> >> I don't claim that it's a deficiency; I claim it's reflective of Swift's
> >> opinionated take on default implementations. Are you, after all, saying that
> >> you have a problem with the addition of _any_ default implementation to an
> >> existing protocol? If so, this conversation isn't about
> synthesis/reflection at
> >> all.
> >
> > No, and you should know that by now. I suggest actually reading some of what I
> > have written as I am sick of repeating myself.
> >
> >>>>>> 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.
> >>>>>
> >>>>> Both options were examined for Codable and for Equatable/Hashable.
> >>>>> The community and core team decided to prefer the current
> design. At
> >>>>> this point, new insights that arise which could not be anticipated
> >>>>> at the time of review could prompt revision. However, so far, you
> >>>>> have presented arguments already considered during review.
> >>>>
> >>>> And so far all I have heard about this is how it was "decided";
> >>>> no-one seems interested in showing how any of these concerns were
> >>>> addressed (if at all), so as far as I can tell they were not,
> or they
> >>>> were wilfully ignored.
> >>>
> >>>
> >>> They were addressed by being considered.
> >>
> >> And yet no-one can apparently summarise what those "considerations" might
> >> be, suggesting that they were either *not* considered at all, or that the
> >> "consideration" was so weak that no-one is willing to step forward to
> >> defend it. Either way it is not sufficient by any reasonable measure.
> >>
> >> If I were to run over your foot in my car, would you be happy to accept
> >> that I "considered" it first?
> >>
> >>
> >> How do you mean? People wrote in with their opinions. Then, taking into
> account
> >> the community's response, the proposal was approved.
> >
> > I mean because not once have you summarised what these alleged
> "considerations"
> > were; if they exist then you should be able do so, yet all I am hearing is "it
> > was considered", which frankly is not an argument at all as it is entirely
> > without substance.
> >
> > If it was genuinely considered then someone should be able to say what points
> > were considered and what conclusions were reached and why. And even if there
> > *was* an earlier decision, that doesn't necessarily make it right. We are
> > discussing it now, and it is clear that any decision that has been made
> has been
> > made poorly at best.
> >
> > And if you're talking about the discussion on Equatable/Hashable specifically,
> > I'm afraid your memory of the "considerations" is radically different to
> mine; as
> > the concerns I raised were essentially ignored, as not a single person gave a
> > justification more substantial than "but, but Codable!" which frankly isn't a
> > justification at all.
> >
> >>>>>> 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.
> >>>>>
> >>>>>
> >>>>> Again, if it applies generally, it must apply specifically.
> What is
> >>>>> "automatically flawed" about the very reasonable synthesized
> default
> >>>>> implementation of ==?
> >>>>
> >>>> It makes the assumption that every equatable property of a type is
> >>>> necessarily relevant to its equality.
> >>>
> >>>
> >>> No necessarily, only provisionally and rebuttably. If it’s not the case,
> >>> override the default.
> >>
> >> So… entirely unlike standard default implementations
> >> which*cannot* "provisionally" assume something is relevant at all,
> >>
> >>
> >> Why not?
> >
> > Because they can only act upon properties/methods that they themselves (or a
> > parent protocol) define. FFS, what is so unclear about that? Or are you
> arguing
> > on this subject without every having actually used a protocol before?
> >
> >> thereby making them entirely different from synthesised/reflective
> >> implementations!
> >>
> >> I'm sorry, but you keep trying to argue that they're the same, but then
> >> admitting that they're not. You can't have it both ways.
> >>
> >>
> >> Well, certainly, synthesized default implementations differ from
> >> non-synthesized ones in key respects. However, they do not differ in terms of
> >> the user experience of conforming to the protocol and having to override the
> >> default.
> >
> > Except that that's not true at all, is it?
> >
> > Synthesised default implementations go much further in how they attempt (and
> > potentially fail) to implement those defaults, and in the specific case of
> > Equatable/Hashable they are fully implementing a protocol without a single
> > property of method being raised as a requirement; they are utterly
> different at a
> > fundamental level, no amount of mental contortion changes that fact.
> >
> >>>> Consider for example if a type stores a collection index for
> >>>> performance reasons; this isn't an intrinsic part of the type, nor
> >>>> relevant to testing equality, yet this default implementation will
> >>>> treat it as such because it*knows nothing about the concrete type's
> >>>> properties*. If a protocol does not define a property then any
> action
> >>>> taken upon such a property is necessarily based upon an assumption;
> >>>> just because it might be fine some of the time, does not make
> it any
> >>>> less flawed.
> >>>>
> >>>> The big difference here between explicit and implicit synthetic
> >>>> implementations is where this assumption originates; if a method is
> >>>> synthesised implicitly then the assumption is made by the protocol
> >>>> designer alone, with no real involvement by the end developer. If I
> >>>> explicitly opt-in to that default however I am signalling to the
> >>>> protocol that it is okay to proceed. In the former case the
> >>>> assumption is unreasonable, in the latter it is explicitly
> >>>> authorised. It is a difference between "I want to make the decision
> >>>> on what's correct" and "I am happy for you (the protocol
> designer) to
> >>>> decide".
> >>>>
> >>>> Right now, when I conform to Equatable, it is a declaration of "I
> >>>> will implement this", but with this retroactive implicit change
> it is
> >>>> now a declaration of "implement this for me", these are two
> entirely
> >>>> different things. Consider; what if I'm working on a piece of code
> >>>> that requires types to be Equatable, but one of the types I'm using
> >>>> currently isn't, so I quickly throw Equatable conformance onto
> it and
> >>>> go back to what I was doing, with the intention of completing
> >>>> conformance later. With this change that type may now receive a
> >>>> default implementation that is wrong, and I've lost the safety net
> >>>> that currently exists.
> >>>
> >>>
> >>> Right now, it still wouldn’t compile, so I don’t see why you would do
> >>> that. In the future, if you want to make it not compile, there is
> nothing
> >>> stopping you from conforming to a non-existent “NotYetEquatable”.
> This was
> >>> something that you asked about earlier and it was answered.
> >>
> >> So your solution is to intentionally write invalid code to work
> around the
> >> fact that a feature is being implemented badly?
> >>
> >>
> >> You stated a use case where you *want* the compiler to stop your code from
> >> compiling by stating a conformance to Equatable without implementing its
> >> requirements. You then stated that the major problem you have with
> synthesized
> >> `==` is that the compiler will now use a default implementation that you
> might
> >> forget about instead of stopping compilation. Therefore, I demonstrated
> how you
> >> could continue to have the compiler stop your code from compiling. It's
> not my
> >> solution that is intentionally writing invalid code; your stated aim was
> to be
> >> able to do so.
> >
> > My stated aim was nothing of the sort.
> >
> > I was pointing out that right now conforming to Equatable means something
> > entirely different from what it will mean in future if this idiotic change
> makes
> > it into release. Please actually read what I write before deciding for
> yourself
> > what my 'stated aim' is.
> >
> > I am *not* asking for workarounds to circumvent a ridiculously flawed
> change to
> > the language, I am arguing why it is flawed and must be changed. If I wanted a
> > workaround I'd do what I'm now seriously considering, which is ditching Swift
> > completely, as I will not use a language if I can no longer trust the team
> > developing it or the decisions that they make.
> >
> >>>> A non-synthesised/reflective implementation cannot strictly be
> >>>> incorrect, because as long as it is implemented properly it will
> >>>> always be correct within the context of the protocol itself. It may
> >>>> not go quite as far as an end developer might want, but that is
> >>>> because they want to add something onto the protocol, not
> because the
> >>>> protocol is wrong.
> >>>>
> >>>> A synthesised/reflective implementation differs because if it goes
> >>>> too far it is wrong not only within the context of the concrete
> type,
> >>>> but also the protocol itself, it is simply incorrect.
> >>>
> >>>
> >>> Again, this is an assertion that misses the mark. If the default
> >>> implementation is unsuitable for a type, it’s unsuitable whether it
> >>> “doesn’t go quite as far” or “goes too far.”
> >>
> >> Because not going quite far enough is not a failure of the protocol, as
> >> protocols by their very nature can only go as far as what they
> define. If a
> >> protocol Foo defines two properties, a method which uses those two
> >> properties correctly, then the method is correct. A developer of a
> concrete
> >> type might want to add more information or tailor the behaviour, but that
> >> doesn't make the default implementation incorrect, it's just considering
> >> the type only within the context of being an instance of Foo.
> >>
> >> Going too far is the opposite; it's the protocol designer messing around
> >> with stuff they do not define at all. It's only ever right by chance, as
> >> it's operating within the context of the concrete type, about which the
> >> protocol does not know anything with certainty.
> >>
> >>
> >> Yes, you have defined "not going far enough" and "going too far" based on
> >> whether an implementation uses only protocol requirements or not.
> However, you
> >> haven't at all demonstrated why this distinction is at all meaningful in
> terms
> >> of the issue you describe with a user conforming to a protocol. If there is a
> >> default implementation, either it returns the expected result for the
> >> conforming type or it does not--those are the only two choices. Are you
> arguing
> >> that, empirically, the default implementation for Equatable will more
> often be
> >> unsuitable for conforming types? If so, what's your evidence?
> >
> > What's yours? If this issue was as "considered" as you constantly claim then
> > where is the evidence that there is no meaningful distinction? Surely such
> > evidence exists, or else the issue hasn't been considered at all, has it?
> >
> > Frankly I am sick of being asked to provide evidence when you are seemingly
> > unwilling to do anything in return, especially when you have conveniently
> ignored
> > every single example that I have already given.
> >
> > It cuts both ways; you claim that "going too far" and "not going far
> enough" are
> > the same thing? Well prove it.
> >
> >>> You state but do not give any rationale for the claim that the former is
> >>> not wrong in some context while the latter is always wrong.
> >>>
> >>> By this line of argumentation, you’d be perfectly content if instead we
> >>> simply had the default implementation of == as “return true” because it
> >>> would be somehow not wrong.
> >>
> >> Only if return true were a reasonable default to give in the context
> of the
> >> protocol, which it clearly is not, as it's not performing any kind of
> >> comparison of equality.
> >>
> >>
> >> Sure it is; `return true` satisfies all the semantic requirements for
> equality:
> >> reflexivity, symmetry, transitivity; and, in the context of the protocol
> which
> >> only provides for this one facility (determination of equality or
> inequality),
> >> any two instances that compare equal _are_ completely interchangeable "within
> >> the context of the protocol itself," as you would say.
> >
> > The purpose of Equatable is to identify types that can be compared for
> equality;
> > returning true does not satisfy that aim because no such comparison is
> occurring,
> > so your example is intentionally ridiculous. Even a less contrived example
> such
> > as comparing memory addresses doesn't fulfil the purpose of Equatable,
> which is
> > all about comparing equality of different instances that might still be
> the same.
> >
> >>>>> 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.
> >>>>
> >>>>
> >>>> If you have to ask for it, then it's not a default. You *are*
> against
> >>>> a default implementation.
> >>>
> >>> A default implementation is an implementation that I, as the
> concrete
> >>> type developer, do not have to provide myself. If you want
> default to
> >>> mean only "automatic" then your attempt to pigeon-hole what I am
> >>> arguing is incorrect, because what I am arguing is then neither
> about
> >>> default implementations nor the means of actually implementing
> it, but
> >>> something else entirely.
> >>>
> >>> But as far as I'm concerned it still absolutely still a default
> >>> implementation whether it is requested or not; the difference is
> I, as
> >>> the end developer, am able to refine what type of defaults that
> I want.
> >>>
> >>>
> >>> The word “default” indicates something that arises in the absence of a
> >>> user indication otherwise.
> >>
> >> Then this proposal is just for a different mechanism for "indicating
> >> otherwise".
> >>
> >> You keep trying to argue that a synthesised/reflective default
> >> implementation is the same as a normal default implementation, yet
> you seem
> >> to be consistently forgetting that even if that is true without this
> >> proposal, that the very proposal itself is to change that, effectively
> >> causing a category of default implementation to become explicitly
> >> opted-into, rather than implicitly. They're still implementations
> that will
> >> be provided automatically, just only when they are permitted to do-so.
> >>
> >>
> >> So to be clear, you are *against* them being the *default*: you wish them
> to be
> >> the *otherwise*.
> >
> > You seem to be insisting upon a narrow definition of default; what I want is
> > control over which types of default implementations are provided. Just because
> > they must be opted-into explicitly does not stop them being "default", as they
> > are still implementations that I myself do not need to implement. The
> difference
> > is that I want to actually *want* them rather than have provided through
> > potentially flimsy assumptions made by a protocol designer. Just because
> there's
> > an extra step doesn't make them any less automatic, otherwise having to
> conform
> > to a protocol in the first place would also prevent them from being defaults.
> >
> > Asking *for* something is more like a middle-ground between the two; the
> > synthetic implementations are still possible defaults, they just aren't
> provided
> > unless you allow them, while omitting the necessary keyword/attribute prevents
> > them being used.
> >
> >>>> On 9 Sep 2017, at 23:17, Gwendal Roué <gwendal.roue at gmail.com
> <mailto:gwendal.roue at gmail.com>
> >>>> <mailto:gwendal.roue at gmail.com
> <mailto:gwendal.roue at gmail.com>>> wrote:
> >>>>
> >>>> All right, I'll be more positive: our science, IT, is a
> >>>> *constructive* science, by *essence*. If there is a problem, there
> >>>> must be a way to show it.
> >>>> It you can't, then there is no problem.
> >>>
> >>> You mean just as I have asked for examples that prove
> >>> non-synthetic/reflective default implementations are as dangerous as
> >>> synthetic/reflective ones? Plenty have suggested this is the
> case yet
> >>> no reasonable examples of that have been given either.
> >>>
> >>> However, examples highlighting problems with the synthesised
> behaviour
> >>> are simple:
> >>>
> >>> structFoo :Equatable{vardata:String}// Currently an error, won't
> >>> be in future
> >>>
> >>>
> >>> Or something a bit more substantial:
> >>>
> >>> structKeyPair :Equatable{
> >>> staticvarcount:Int=0
> >>>
> >>> varcount:Int
> >>> letkey:String// This is the only property that should be
> equatable
> >>> varvalue:String
> >>>
> >>> init(key:String, value:String) {
> >>> letcount =KeyPair.count&+1
> >>> KeyPair.count= count;self.count= count
> >>> self.key= key;self.value= value
> >>> }
> >>> }
> >>>
> >>> Here the only important property in the key pair is the key, the
> value
> >>> isn't important (only the keys are to be considered unique) and the
> >>> count is just a throwaway value. The synthesised default
> >>> implementation for this concrete type will therefore be completely
> >>> wrong, likewise for Hashable, which will likely produce radically
> >>> different results for instances that should be the same.
> >>
> >> I notice that despite asking endlessly for examples, the ones I've given
> >> are being ignored. In future I shall remind people asking for examples
> >> where they can shove them.
> >
> > And once again, totally ignored. You seem to love asking for "evidence"
> but why
> > exactly should I bother giving anything if you ignore it when I try to?
> >
> > _______________________________________________
> > swift-evolution mailing list
> > swift-evolution at swift.org <mailto:swift-evolution at swift.org>
> <mailto:swift-evolution at swift.org <mailto:swift-evolution at swift.org>>
> > https://lists.swift.org/mailman/listinfo/swift-evolution
> > <https://lists.swift.org/mailman/listinfo/swift-evolution>
> >
> >
> >
> >
> > _______________________________________________
> > swift-evolution mailing list
> > swift-evolution at swift.org <mailto:swift-evolution at swift.org>
> > https://lists.swift.org/mailman/listinfo/swift-evolution
> >
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
More information about the swift-evolution
mailing list