On 12.09.2017 0:35, Tony Allevato wrote:


On Mon, Sep 11, 2017 at 2:05 PM Vladimir.S via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

    On 11.09.2017 21:55, Thorsten Seitz via swift-evolution wrote:
     > I think I do understand Haravikk's argument (actually it seems quite
    straightforward
     > to me).
     >
     > An example should be:
     >
     > struct Foo : Equatable {
     >      var x: Int
     >      var cachedLabel: String? = nil
     >
     >      init(x: Int) {
     >          self.x = x
     >      }
     >
     >      mutating func label() {
     >          if let label = cachedLabel {
     >              return label
     >          }
     >          let label = calculateLabel()
     >          cachedLabel = label
     >          return cachedLabel
     >      }
     > }
     >
     > var foo1 = Foo(x: 1)
     > var foo2 = Foo(x: 1)
     > foo1 == foo2 // true
     > var label = foo1.label()
     > foo1 == foo2 // now false, due to cachedString being falsely included in 
the
    comparison
     >
     > The problem is that the developer was not required to implement the 
protocol
    and so
     > might forget it.
     > The difference to other default implementations is that those use the 
protocol
    itself
     > as building blocks and so are correct with regards to the protocol's 
semantics,
     > whereas the synthesized equality reaches deeply into the private innards 
of a
    struct
     > and therefore is much more likely to be wrong as in the example above.
     >
     > Why not just write
     >
     > *struct* Foo : *deriving* Equatable {...}
     >
     > to request the synthesized implementation?

    FWIW, +100. The same should be required for Codable. I support the opinion 
that
    'synthesized' methods differs from protocol-default-implementation in what 
'kind' of
    data they use: defined by protocol itself or internals of the conformed 
type. And
    this can lead to more un-expected problems.

    If protocol is able to synthesize its requirements, it should require a
    'deriving'-like marker when type conforms to it to make it absolutely clear 
what
    happens here. It would be not a confusion point, but clarify the intention 
to better
    understand the code.

    Thinking about *future* custom protocols that could implement requirements 
in default
    implementation by using macros/reflection, for me it seems like such 
protocol should
    *also* somehow explicitly state that some requirements are 
auto-synthesized, probably
    by conforming(derive) to some compiler-magic protocol 'AutoSynthesize'.
    (i.e. 'protocol MySynthesizeable: AutoSynthesize {...}')

    So each built-in protocol like Equatable/Hashable/Codable will conform to 
it, and
    also, each custom "auto-synthesizeable" protocol - also should explicitly 
conform to
    AutoSynthesize. So, when type conforms to it - such type should use 
'deriving'-like
    marker if auto-generation of methods is expected.


This doesn't align with how Swift views the role of protocols, though. One of the criteria that the core team has said they look for in a protocol is "what generic algorithms would be written using this protocol?" AutoSynthesize doesn't satisfy that—there are no generic algorithms that you would write with AutoEquatable that differ from what you would write with Equatable.


OK, got it, it was general thoughts, not exact proposal regarding the AutoSynthesize protocol. Probably it should be @autosynthesize directive for protocol when you define it or other 'marker', so when you conform to this protocol, you *can* explicitely use 'derived'-like keyword to make requirements auto-synthesized, otherwise you'll be asked by compiler for manual implementation.


    I also have a question regarding future direction of 'exclusion' of fields 
from being
    included into auto-generated implementation of 
Equatable/Hashable/Codable/other.

    If we'll have this 'deriving'-like marker, it seems naturally if we mark 
some member
    with some kind of '@noderiving' marker, like here:

    struct Foo : deriving Equatable {
            var x: Int
            var y: Int
            var z: Int
            @noderiving var cachedLabel: String? = nil
    }

    this @noderiving directive will work for protocols based on AutoSynthesize 
magic
    protocol. I.e., if you construct your own protocol with auto-synthesizeable 
methods,
    to be able to *know* which members should be 'excluded' for your 
implementation, you
    should base your protocol on AutoSynthesize protocol.


This is something I mention in the original proposal, and I agree that it would be nice to have added later since there are clear known use cases where it's important.

However, the feature shouldn't be tied *specifically* to derived implementations (meaning it shouldn't be named that way). What we're really talking about is "transient" data—data that exists for the purposes of caching/performance/etc. but which does not actually contribute to the thing's "value".

The fact that transient data should not be ignored for equality, hashing, and serialization just happens to align with the protocols that we auto-synthesize so far, but it's not necessarily limited to those use cases. If an attribute is added for something like this, it should be *semantic* rather than speak to implementation details. In other words, it would be inappropriate to say "exclude this property from synthesized operations", but it would be fine to say "this property is transient data" and it just so happens that Equatable, Hashable, and Codable use that information to control what they synthesize.

All this is a subtle, but important, distinction. One day, when Swift has the ability to introspect metadata about a type and its properties, someone may want to use a hypothetical "transient" attribute for something wholly unrelated to synthesis.

I see your points, but is it not possible that we want to exclude some property for Equatable but keep it for Codable, or vise-versa, for example? So, actually, IMO we need a way to exclude property from some specific protocol/protocols. Like

@transient var ...
@transient(for:Equatable) var ...
@transient(for:Codable) var ...
@transient(for:SomeOtherAuto) var ...

And again, I'm not proposing some concrete syntax/keyword, but think we'll need(in future,yes, but IMO better to discuss now) a way to 'exclude' specific property from specific auto-synthesizeable protocol.


Just my 2 cents why I believe we need an explicit 'derived'-like keyword when we want auto-synthesized requirement for protocol:

* This will not change the logic/behavior of 'normal' conformance to Equatable/Hashable, i.e. if you conform the type to them - you need to provide an implementation OR (new text in warning message will be added) "use 'derived' keyword to "auto-synthesize" these methods. I.e. auto-synthesize will be something separate, additional, that you can use if you want it, your clear choose.
It will not interfere with your 'usual' model of protocol conformance.

* You explicitly mark which protocol you want to be synthesized for you. For example, if I see "struct S: derived Equatable, Codable" - I understand that Equatable will be "generated" and Codable will be implemented manually in code.

* This makes code more clean, more understandable by reader, less error-prone, more explicit on intention. The code *will* be better.

* Without explicit 'derived'-like keyword, the code will be worse : less explicit about intention, more error-prone, hiding important details from reader, etc.

* And the 'derived'-like keyword is good price to have better code in this case.

Just my IMOs. Thank you for reading.

Vladimir.



    I hope this makes any sense :-)

    Vladimir.

     >
     > -Thorsten
     >
     >
     > Am 09.09.2017 um 19:42 schrieb Xiaodi Wu via swift-evolution
     > <swift-evolution@swift.org <mailto:swift-evolution@swift.org>
    <mailto:swift-evolution@swift.org <mailto:swift-evolution@swift.org>>>:
     >
     >>
     >> On Sat, Sep 9, 2017 at 06:41 Haravikk via swift-evolution
     >> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>
    <mailto:swift-evolution@swift.org <mailto:swift-evolution@swift.org>>> 
wrote:
     >>
     >>>     On 9 Sep 2017, at 09:33, Xiaodi Wu <xiaodi...@gmail.com
    <mailto:xiaodi...@gmail.com>
     >>>     <mailto: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>
    <mailto: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>
     >>>>         <mailto: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>
     >>>>         <mailto: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>
    <mailto: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>
     >>>>>>             <mailto: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:
     >>>
     >>>          1. 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.
     >>
     >>
     >> You are against a default implementation for ==, i.e. an implementation 
that is
     >> provided for you if you conform a type to the protocol and do nothing 
else
     >> ("implicitly rather than explicitly"), and you are against the default
     >> implementation being on the existing protocol Equatable ("retroactive
    change"). So,
     >> to summarize, what you are against is precisely a default 
implementation for
    the ==
     >> requirement on Equatable.
     >>
     >> This is the topic of discussion here; I am attempting to convince you 
that you
     >> should be for rather than against these things.
     >>
     >>
     >>>     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).
     >>
     >>
     >> Right, one must consider the semantics of the specific protocol 
requirement
    and ask
     >> whether a reasonable default can be provided for it.
     >>
     >>     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).
     >>
     >>
     >> I am confused why you are trying to argue in general terms about the 
universe of
     >> all possible default implementations that use reflection. This is 
necessarily a
     >> more difficult argument to make, and if it is to be convincing for all 
default
     >> implementations it must also be convincing for the two specific protocol
     >> requirements we are talking about here. Start small:
     >>
     >> We have agreed, as a community, that there is a reasonable default 
implementation
     >> for Equatable.== when certain conditions are met (for value types only 
at the
     >> moment, I believe). Namely, given two values of a type that has only 
Equatable
     >> stored properties, those values are equal if their stored properties 
are all
    equal.
     >> The author of a new value type who wishes to make her type Equatable 
but chooses
     >> not to implement a custom == then benefits from this default when all 
stored
     >> properties are Equatable.
     >>
     >>     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.
     >>
     >>>     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 
==?
     >>
     >>>         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.
     >>
     >>
     >> That is not the argument. The argument is that they are 
indistinguishable in the
     >> sense that the author of a type who intends to supply a custom 
implementation but
     >> neglects to do so will have a default implementation supplied for them. 
It is
     >> plainly true that this is no more or less likely to happen simply 
because the
     >> default implementation is synthesized.
     >>
     >>>     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.
     >>
     >>     _______________________________________________
     >>     swift-evolution mailing list
     >> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
    <mailto:swift-evolution@swift.org <mailto:swift-evolution@swift.org>>
     >> https://lists.swift.org/mailman/listinfo/swift-evolution
     >>
     >> _______________________________________________
     >> swift-evolution mailing list
     >> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
    <mailto:swift-evolution@swift.org <mailto:swift-evolution@swift.org>>
     >> https://lists.swift.org/mailman/listinfo/swift-evolution
     >
     >
     > _______________________________________________
     > swift-evolution mailing list
     > swift-evolution@swift.org <mailto:swift-evolution@swift.org>
     > https://lists.swift.org/mailman/listinfo/swift-evolution
     >
    _______________________________________________
    swift-evolution mailing list
    swift-evolution@swift.org <mailto: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