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:

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@swift.org <mailto:swift-evolution@swift.org>> wrote:


    On 13 Sep 2017, at 03:26, Xiaodi Wu <xiaodi...@gmail.com
    <mailto:xiaodi...@gmail.com>> wrote:

    On Tue, Sep 12, 2017 at 11:43 AM, Haravikk via
    swift-evolution<swift-evolution@swift.org 
<mailto:swift-evolution@swift.org>>wrote:


        On 12 Sep 2017, at 12:08, Xiaodi Wu <xiaodi...@gmail.com
        <mailto:xiaodi...@gmail.com>> wrote:

        On Mon, Sep 11, 2017 at 06:03 Haravikk via swift-evolution
        <swift-evolution@swift.org <mailto:swift-evolution@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.r...@gmail.com
            <mailto:gwendal.r...@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@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

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to