[swift-evolution] [Proposal] Explicit Synthetic Behaviour

Tony Allevato tony.allevato at gmail.com
Wed Sep 13 12:48:25 CDT 2017


On Wed, Sep 13, 2017 at 10:21 AM Vladimir.S via swift-evolution <
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.



>
> 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>> wrote:
> >
> >
> >>     On 13 Sep 2017, at 03:26, Xiaodi Wu <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>>wrote:
> >>
> >>
> >>>         On 12 Sep 2017, at 12:08, Xiaodi Wu <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>>
> 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>> 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>
> >     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
> > 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/20170913/3bc5e6ce/attachment.html>


More information about the swift-evolution mailing list