>
>
>
> >
> >
> >
> > On Tue, Sep 12, 2017 at 9:58 AM, Thorsten Seitz via
swift-evolution
> > <swift-evolution@swift.org <mailto:
swift-evolution@swift.org>
> <mailto:swift-evolution@swift.org <mailto:swift-evolution@swift.org>>>
wrote:
> >
> > Good arguments, Tony, you have convinced me on all
points.
> Transient is
> > the way to go. Thank you for your patience!
> >
> >
> > On many points, I agree with Tony, but I disagree that
"transient"
> addresses
> > the issue at hand. The challenge being made is that, as
Gwendal puts
> it, it's
> > _unwise_ to have a default implementation, because people
might forget
> that
> > there is a default implementation. "Transient" only works
if you remember
> > that there is a default implementation, and in that case,
we already
> have a
> > clear syntax for overriding the default.
> >
> >
> > RightБ─■I hope it hasn't sounded like I'm conflating the two
concepts
> completely.
> > The reason I brought up "transient" is because nearly all of
the "risky"
> examples
> > being cited so far have been of the variety "I have a type
where some
> properties
> > happen to be Equatable but shouldn't be involved in
equality", so my intention
> > has been to show that if we have a better solution to that
specific problem
> > (which is, related to but not the same as the question at
hand), then there
> > aren't enough risky cases left to warrant adding this level
of complexity
> to the
> > protocol system.
> >
> >
> > As others point out, there's a temptation here to write
things like
> > "transient(Equatable)" so as to control the synthesis of
> implementations on a
> > per-protocol basis. By that point, you've invented a
whole new syntax for
> > implementing protocol requirements. (Ah, you might say,
but it's hard to
> > write a good hashValue implementation: sure, but that's
adequately
> solved by
> > a library-supplied combineHashes() function.)
> >
> >
> > I totally agree with this. A design that would try to
annotate "transient"
> with a
> > protocol or list of protocols is missing the point of the
semantics that
> > "transient" is supposed to provide. It's not a series of
switches to that
> can be
> > flipped on and off for arbitrary protocolsБ─■it's a semantic
tag that assigns
> > additional meaning to properties and certain protocols (such
as Equatable,
> > Hashable, and Codable, but possibly others that haven't been
designed yet)
> would
> > have protocol-specific behavior for those properties.
> >
> > To better explain what I've been poking at, I'm kind of
extrapolating this
> out to
> > a possible future where it may be possible to more generally
(1) define custom
> > @attributes in Swift, like Java annotations, and then (2) use
some
> > metaprogramming constructs to generate introspective default
> implementations for
> > a protocol at compile-time just as the compiler does
"magically" now, and the
> > generator would be able to query attributes that are defined
by the same
> library
> > author as the protocol and handle them accordingly.
> >
> > In a world where that's possible, I think it's less helpful
to think in
> terms of
> > "I need to distinguish between conforming to X and getting a
synthesized
> > implementation and conforming to X and avoiding the
synthesized implementation
> > because the default might be risky", but instead to think in
terms of "How
> can I
> > provide enough semantic information about my types to remove
the risk?"
> >
> > In other words, the switches we offer developers to flip
shouldn't be about
> > turning on/off entire features, but about giving the compiler
enough
> information
> > to make it smart enough that we never need to turn it off in
the first
> place. As
> > I alluded to before, if I have 10 properties in a type and
only 1 of those
> needs
> > to be ignored in ==/hashValue/whatever, writing "Equatable"
instead of
> "derives
> > Equatable" isn't all that helpful. Yes, it spits out an error
message
> where there
> > wouldn't have been one, but it doesn't reduce any of the
burden of having to
> > provide the appropriate manual implementation.
> >
> > But all that stuff about custom attributes and metaprogramming
> introspection is a
> > big topic of it's own that isn't going to be solved in Swift
5, so this is
> a bit
> > of a digression. :)
> >
> >
> > That said, we could have enums EquatingKeys and HashingKeys, a la
> CodingKeys... That
> > may not be a huge leap to propose and implement.
>
> Actually, not taking into account a question of explicit marker for
auto-generated
> methods, this is IMO a great point.
>
> Codable, which can auto-generate methods, *had* these CodingKeys
from the moment of
> birth. Currently, we have a proposal for auto-generating of methods
for
> Equatable/Hashable. Why we don't have a EquatingKeys/HashingKeys
option for them in
> symmetry with Codable? Why Codable already has a method to exclude
fields, but for
> Equatable/Hashable we are discussing some future esoteric
'@transient' modifier(which
> should describe the behaviour and destination of the property in
details for compiler
> and conformed protocols so all will "just work") ?
> How this future '@transient' will live together with current
CodingKeys ?
>
> IMO the right solution will be:
> 1. introduce 'deriving'-like keyword to explicitly express that you
request an
> auto-synthesize of protocol requirements
> 2. introduce EquatingKeys/HashingKeys to be able to say which
properties should be
> included in generated requirements
> 3. Think what kind of '@transient' marker could be introduced in
future to replace
> the using of CodingKeys/EquatingKeys/HashingKeys.
>
>
> I don't agree with Xiaodi here that a hypothetical
EquatableKey/HashableKey would be
> the same thing as CodingKey.
>
> CodingKey embeds other semantic information, like the string or integer
key to use
> when encoding/decoding a value in a container. The cases of a CodingKey
enum are also
> actual values that are passed around to other encoder/decoder
methods—they're not
> *just* there for the synthesized code.
>Can't agree. Yes, they are not *just* for the synthesized code, they have
additional
meaning for _Codable_ protocol. But if you read thisEncoding and Decoding Custom Types | Apple Developer Documentation
, first you find regarding CodingKeys is :
"
Choose Properties to Encode and Decode Using Coding KeysWhen this enumeration is present, its cases serve as the authoritative
list of
properties that must be included when instances of a codable type are
encoded or
decoded. The names of the enumeration cases should match the names you've
given to
the corresponding properties in your type.Omit properties from the CodingKeys enumeration if they won't be present
when
decoding instances, or if certain properties shouldn't be included in an
encoded
representation.
"So I'd say, that exclusion of fields from participate in Codable, is main
or at least
high important role of CodingKeys. And I can't see why
EquatableKeys/HashableKeys
can't play the same role(without additional meaning CodingKeys has for
Codable).
Well, no. You can't just brush aside all the conditions where using a type
doesn't make sense because there's one place where they act similarly.
CodingKeys contain *data*. That data is used at *runtime*. They are
legitimate values that are passed around as the inputs to other functions
like the encoders and decoders. It's true that the compiler uses that enum
to decide what to synthesize, but the compiler also uses *the values
themselves* in that synthesized implementation.
The hypothetical Equatable/HashableKeys enum would not contain any data. It
would not be used at runtime. It would not be passed around as the inputs
to other functions. It would solely be used by the compiler to determine
which properties were included or excluded in synthesis, but the compiler
would never use its values in the synthesized implementation. By that
definition, it simply does not make sense for it to be a type in the type
system at all, with all the additional type metadata and runtime support
that making it so would bring. If no user—not even the synthesized
implementation—would ever instantiate or operate on that type in any
meaningful way, then there is no reason for it to exist.
···
On Wed, Sep 13, 2017 at 10:03 AM Vladimir.S <svabox@gmail.com> wrote:
On 13.09.2017 19:03, Tony Allevato wrote:
> On Wed, Sep 13, 2017 at 8:41 AM Vladimir.S via swift-evolution > > <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
> On 13.09.2017 7:14, Xiaodi Wu via swift-evolution wrote:
> > On Tue, Sep 12, 2017 at 22:07 Tony Allevato < > tony.allevato@gmail.com > > <mailto:tony.allevato@gmail.com> > > > <mailto:tony.allevato@gmail.com <mailto:tony.allevato@gmail.com>>> > wrote:
> > On Tue, Sep 12, 2017 at 7:10 PM Xiaodi Wu < > xiaodi.wu@gmail.com > > <mailto:xiaodi.wu@gmail.com> > > > <mailto:xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>>> > wrote:
Vladimir.
> Equatable/HashableKeys wouldn't be that. The cases would never be used
anywhere else,
> passed anywhere, or returned from anything. It would just be a list of
cases that
> match property names, which doesn't really feel like the right model
here. Simply
> put, if the values aren't useful *as values*, they shouldn't be modeled
as values.
>
> I also don't see how "transient" would ever replace CodingKeys. Instead,
it would
> augment it. Right now, the compiler autogenerates CodingKeys if you
don't provide
> one. What "transient" would do is affect *what* the compiler generates
for you there;
> likewise with Equatable and Hashable. They're not identical concepts.
>
> Vladimir.
>
> >
> > -Thorsten
> >
> > Am 12.09.2017 um 16:38 schrieb Tony Allevato via > swift-evolution > > > <swift-evolution@swift.org <mailto:
swift-evolution@swift.org>
> <mailto:swift-evolution@swift.org <mailto:swift-evolution@swift.org
>>>:
> >
> >>
> >>
> >> On Mon, Sep 11, 2017 at 10:05 PM Gwendal Rouц╘ > > <gwendal.roue@gmail.com <mailto:gwendal.roue@gmail.com> > > >> <mailto:gwendal.roue@gmail.com <mailto: > gwendal.roue@gmail.com>>> > > wrote:
> >>
> >>>
> >>>> 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.
> >>>
> >>> And so everybody has to swallow implicit
and non-avoidable
> >>> code synthesis and shut up?
> >>>
> >>>
> >>> That's not what I said. I simply pointed out
one of the barriers
> >>> to getting a new protocol added to the language.
> >>>
> >>> Code synthesis is explicitly opt-in and quite
> avoidableБ─■you either
> >>> don't conform to the protocol, or you conform
to the
> protocol and
> >>> provide your own implementation. What folks are
differing on is
> >>> whether there should have to be *two* explicit
switches that you
> >>> flip instead of one.
> >>
> >> No. One does not add a protocol conformance by
whim. One adds a
> >> protocol conformance by need. So the conformance
to the
> protocol is
> >> a *given* in our analysis of the consequence of
code
> synthesis. You
> >> can not say "just don't adopt it".
> >>
> >> As soon as I type the protocol name, I get
synthesis. That's the
> >> reason why the synthesized code is implicit. The
synthesis is
> >> explicitly written in the protocol
documentation, if you
> want. But
> >> not in the programmer's code.
> >>
> >> I did use "non-avoidable" badly, you're right:
one can avoid
> it, by
> >> providing its custom implementation.
> >>
> >> So the code synthesis out of a mere protocol
adoption *is*
> implicit.
> >>
> >>> Let's imagine a pie. The whole pie is the set
of all Swift
> types.
> >>> Some slice of that pie is the subset of those
types that satisfy
> >>> the conditions that allow one of our protocols
to be
> synthesized.
> >>> Now that slice of pie can be sliced again, into
the subset of
> >>> types where (1) the synthesized implementation
is correct
> both in
> >>> terms of strict value and of business logic,
and (2) the subset
> >>> where it is correct in terms of strict value
but is not the
> right
> >>> business logic because of something like
transient data.
> >>
> >> Yes.
> >>
> >>> What we have to consider is, how large is slice
(2) relative to
> >>> the whole pie, *and* what is the likelihood
that developers are
> >>> going to mistakenly conform to the protocol
without providing
> >>> their own implementation, *and* is the added
complexity worth
> >>> protecting against this case?
> >>
> >> That's quite a difficult job: do you think you
can evaluate this
> >> likelihood?
> >>
> >> Explicit synthesis has big advantage: it avoids
this question
> entirely.
> >>
> >> Remember that the main problem with slide (2) is
that developers
> >> can not *learn* to avoid it.
> >>
> >> For each type is slide (2) there is a
probability that it comes
> >> into existence with a forgotten explicit
protocol adoption. And
> >> this probability will not go down as people
learn Swift and
> >> discover the existence of slide (2). Why?
because this
> probability
> >> is driven by unavoidable human behaviors:
> >> - developer doesn't see the problem (a
programmer mistake)
> >> - the developper plans to add explicit
conformance later and
> >> happens to forget (carelessness)
> >> - a developper extends an existing type with a
transient
> property,
> >> and doesn't add the explicit protocol
conformance that has become
> >> required.
> >>
> >> Case 2 and 3 bite even experienced developers.
And they can't be
> >> improved by learning.
> >>
> >> Looks like the problem is better defined as an
ergonomics
> issue, now.
> >>
> >>> If someone can show me something that points to
accidental
> >>> synthesized implementations being a significant
barrier to
> smooth
> >>> development in Swift, I'm more than happy to
consider that
> >>> evidence. But right now, this all seems
hypothetical ("I'm
> worried
> >>> that...") and what's being proposed is adding
complexity to the
> >>> language (an entirely new axis of protocol
conformance) that
> would
> >>> (1) solve a problem that may not exist to any
great degree, and
> >>> (2) does not address the fact that if that
problem does indeed
> >>> exist, then the same problem just as likely
exists with certain
> >>> non-synthesized default implementations.
> >>
> >> There is this sample code by Thorsten Seitz with
a cached
> property
> >> which is quite simple and clear :
> >>
>
[swift-evolution] [Proposal] Explicit Synthetic Behaviour
> >>
> >> This is the sample code that had me enter the
"worried" camp.'
> >>
> >>
> >> I really like Thorsten's example, because it
actually proves that
> >> requiring explicit derivation is NOT the correct
approach here.
> (Let's
> >> set aside the fact that Optionals prevent synthesis
because we don't
> >> have conditional conformances yet, and assume that
we've gotten that
> >> feature as well for the sake of argument.)
> >>
> >> Let's look at two scenarios:
> >>
> >> 1) Imagine I have a value type with a number of
simple Equatable
> >> properties. In a world where synthesis is explicit,
I tell that value
> >> type to "derive Equatable". Everything is fine.
Later, I decide
> to add
> >> some cache property like in Thorsten's example, and
that property
> just
> >> happens to also be Equatable. After doing so, the
correct thing to do
> >> would be to remove the "derive" part and provide my
custom
> >> implementation. But if I forget to do that, the
synthesized operator
> >> still exists and applies to that type. If you're
arguing that "derive
> >> Equatable" is better because its explicitness
prevents errors,
> you must
> >> also accept that there are possibly just as many
cases where that
> >> explicitness does *not* prevent errors.
> >>
> >> 2) Imagine I have a value type with 10 Equatable
properties and one
> >> caching property that also happens to be Equatable.
The solution
> being
> >> proposed here says that I'm better off with explicit
synthesis
> because
> >> if I conform that type to Equatable without
"derive", I get an error,
> >> and then I can provide my own custom implementation.
But I have to
> >> provide that custom implementation *anyway* to
ignore the caching
> >> property even if we don't make synthesis explicit.
Making it explicit
> >> hasn't saved me any workБ─■it's only given me a
compiler error for a
> >> problem that I already knew I needed to resolve. If
we tack on
> Hashable
> >> and Codable to that type, then I still have to write
a significant
> >> amount of boilerplate for those custom operations.
Furthermore, if
> >> synthesis is explicit, I have *more* work because I
have to
> declare it
> >> explicitly even for types where the problem above
does not occur.
> >>
> >> So, making derivation explicit is simply a
non-useful dodge that
> >> doesn't solve the underlying problem, which is this:
Swift's type
> >> system currently does not distinguish between
Equatable
> properties that
> >> *do* contribute to the "value" of their containing
instance vs.
> >> Equatable properties that *do not* contribute to the
"value" of their
> >> containing instance. It's the difference between
behavior based on a
> >> type and additional business logic implemented on
top of those types.
> >>
> >> So, what I'm trying to encourage people to see is
this: saying "there
> >> are some cases where synthesis is risky because it's
incompatible
> with
> >> certain semantics, so let's make it explicit
everywhere" is trying to
> >> fix the wrong problem. What we should be looking at
is *"how do
> we give
> >> Swift the additional semantic information it needs
to make the
> >> appropriate decision about what to synthesize?"*
> >>
> >> That's where concepts like "transient" come in. If I
have an
> >> Equatable/Hashable/Codable type with 10 properties
and one cache
> >> property, I *still* want the synthesis for those
first 10
> properties. I
> >> don't want the presence of *one* property to force
me to write all of
> >> that boilerplate myself. I just want to tell the
compiler which
> >> properties to ignore.
> >>
> >> Imagine you're a stranger reading the code to such a
type for the
> first
> >> time. Which would be easier for you to quickly
understand? The
> version
> >> with custom implementations of ==, hashValue,
init(from:), and
> >> encode(to:) all covering 10 or more properties that
you have to read
> >> through to figure out what's being ignored (and make
sure that the
> >> author has done so correctly), or the version that
conforms to those
> >> protocols, does not contain a custom implementation,
and has each
> >> transient property clearly marked? The latter is
more concise and
> >> "transient" carries semantic weight that gets buried
in a handwritten
> >> implementation.
> >>
> >> Here's a fun exerciseБ─■you can actually write
something like
> "transient"
> >> without any additional language support today:
> >>
https://gist.github.com/allevato/e1aab2b7b2ced72431c3cf4de71d306d\. A
> >> big drawback to this Transient type is that it's not
as easy to
> use as
> >> an Optional because of the additional sugar that
Swift provides
> for the
> >> latter, but one could expand it with some helper
properties and
> methods
> >> to sugar it up the best that the language will allow
today.
> >>
> >> I would wager that this concept, either as a wrapper
type or as a
> >> built-in property attribute, would solve a
significant majority of
> >> cases where synthesis is viewed to be "risky". If we
accept that
> >> premise, then we can back to our slice of pie and
all we're left with
> >> in terms of "risky" types are "types that contain
properties that
> >> conform to a certain protocol but are not really
transient but also
> >> shouldn't be included verbatim in synthesized
operations". I'm
> >> struggling to imagine a type that fits that
description, so if
> they do
> >> exist, it's doubtful that they're a common enough
problem to warrant
> >> introducing more complexity into the protocol
conformance system.
> >>
> >>
> >> Gwendal
> >>
> >> _______________________________________________
> >> 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
>