> On Dec 31, 2015, at 9:47 AM, Matthew Johnson <matt...@anandabits.com> wrote: > >> >> On Dec 31, 2015, at 11:18 AM, Dave Abrahams <dabrah...@apple.com >> <mailto:dabrah...@apple.com>> wrote: >> >>> >>> On Dec 31, 2015, at 9:01 AM, Matthew Johnson <matt...@anandabits.com >>> <mailto:matt...@anandabits.com>> wrote: >>> >>> >>> >>> Sent from my iPad >>> >>> On Dec 31, 2015, at 10:09 AM, Dave Abrahams <dabrah...@apple.com >>> <mailto:dabrah...@apple.com>> wrote: >>> >>>> >>>> -Dave >>>> >>>>> On Dec 31, 2015, at 7:33 AM, Matthew Johnson via swift-evolution >>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote: >>>>> >>>>>> >>>>>> On Dec 31, 2015, at 5:04 AM, Tino Heth <2...@gmx.de >>>>>> <mailto:2...@gmx.de>> wrote: >>>>>> >>>>>> >>>>>>> I don’t want this thread to get distracted with memberwise >>>>>>> initialization >>>>>> Makes sense in general, but Kotlin solves those problems as a whole, and >>>>>> the major benefit of their approach is that everything fits together >>>>>> really fine. >>>>>> But I'll skip everything that is not related to forwarding. >>>>>> >>>>>>> One approach I considered would look like this: >>>>>>> >>>>>>> class Forwarder: P { >>>>>>> // I didn’t like `implements` but didn’t have a better idea before I >>>>>>> abandoned this approach >>>>>>> var forwardee: Forwardee implements P >>>>>>> } >>>>>> Honestly, I'm can't see the value this discarded variant has for this >>>>>> discussion… you have to pit your proposal against Kotlin if you really >>>>>> want to convince anyone of its superiority. >>>>>> >>>>>>> With the memberwise initialization proposal you also have the >>>>>>> initializer synthesized automatically. The only thing it doesn’t do >>>>>>> that your Kotlin example does is automatically declare conformance. >>>>>>> This was an intentional design decision because it allows for >>>>>>> additional expressivity. This is addressed in the alternatives >>>>>>> considered section of the proposal. >>>>>> Can you be more precise? Kotlin clearly states what a class is doing in >>>>>> its first line, with all expressivity that is necessary by practical >>>>>> means. >>>>> >>>>> What I mean is this. In the example you gave and the syntax Kotlin uses: >>>>> >>>>> classs Forwarder(forwardee: Forwardee): P by forwardee {} >>>>> >>>>> Forwarding is coupled to protocol conformance. This means I cannot use >>>>> forwarding without conforming to the protocol that is forwarded. >>>>> >>>>> Here is a quick example using the syntax of my proposal to demonstrate >>>>> the difference: >>>>> >>>>> class Forwarder { >>>>> let forwardee: Forwardee >>>>> forward P to forwardee >>>>> } >>>>> >>>>> vs >>>>> >>>>> class Forwarder: P { >>>>> let forwardee: Forwardee >>>>> forward P to forwardee >>>>> } >>>>> >>>>> In the first example Forwarder does not conform to P. Forwarding is only >>>>> used to synthesize the members of P. I am greatly expanding the >>>>> motivation section of the proposal and will have examples showing where >>>>> this is what you want. The lazy collections section I posted last night >>>>> includes the first examples where this is the case. >>>>> >>>>> In the second example here Forwarder does conform to P. The author of >>>>> Forwarder has the flexibility to specify whether conformance is desired >>>>> or not. >>>> >>>> There are ways to handle that, including factoring the APIs of interest >>>> out of P and into a private protocol Q, then declaring the Forwardee’s >>>> conformance to Q. Now, there’s an expressivity problem with our current >>>> access control system that you can’t use an internal or private protocol >>>> to provide public API, but that should be fixed separately. >>> >>> I'm not sure where Q comes into play in this specific example. The idea >>> here is that forwarding implementations of all members of P are synthesized >>> by the forward declaration. It is left up to Forwarder to decide whether >>> or not to declare actual conformance to P. I am also confused by "then >>> declaring the Forwardee’s conformance to Q" because we are discussing >>> Forwarder's conformance here, not Forwardee’s. >> >> Presumably both ends of the forwarding arrangement would have to conform to >> the same protocol, no? > > No. This is addressed in the proposal and the lazy collections motivating > example I replied with last night. I don’t think it’s a good idea to require > this. > > The forwardee needs to implement the members of the protocol but does not > need to conform. The forwarder will receive forwarding implementations of > the members, but again does not need to declare conformance. Forwarding is > orthogonal to conformance, just as it is today when you manually write > forwarding members today. > >> >>> What do you have in mind when you mention using a private or internal >>> protocol to provide public API? It sounds like that might be interesting >>> but I'm having trouble imagining what the syntax would look like and >>> exactly how it would work. Is this something that is planned? >> >> Not planned, but desired. >> >>> What might it look like? >> >> Details need to be worked out. One thing we were doing for a while in the >> stdlib, before the rules got tightened and made it impossible, was >> >> struct X : PublicProtocol, PrivateProtocol { >> ... >> } >> >> extension PublicProtocol where Self : PrivateProtocol { >> // API that uses only PublicProtocol in its implementation here >> } > > Presumably you implement public API here where members of PrivateProtocol can > be accessed? This would address the problem of leaking implementation > details when adding default forwarding implementations so it is definitely > better than current state. > >> >>> In any case, I don't see why that is related to requiring a Forwarder to >>> conform to the forwarded protocol. There doesn't appear to me to be a good >>> reason to require that and there are reasons not to require it. Protocols >>> enable and drive the forwarding member synthesis mechanism but that >>> mechanism doesn't need to require or provide conformance. It is a third >>> major way to use protocols in addition to generic constraints and >>> existential types. >> >> Of course I could be wrong, but my instincts tell me that is an unneeded >> dimension of complexity, which is why I am resisting it. The >> generic/existential duality is already problematic in some ways, IMO. > > I don’t really understand what is complex about it. Can you elaborate on why > you think it introduces complexity? The idea seems pretty simple to me - if > you can write a forwarding implementation that compiles and works the > compiler should be able to synthesize it for you. I don’t see any reason to > disallow that. > > Is your concern that we may not be able to support forwarding of every > protocol someone might come up with and there would be confusion around what > protocols are eligible for forwarding and which aren’t (similar to the > current confusion around which protocols can be used as existentials and > which can’t)?
No, it just feels like an ad-hoc use of protocols to use their APIs as constraints without creating a conformance. > Isn’t the primary problem with generic / existential the fact that > existentials are pretty limited currently? This is an unfortunate limitation > and my understanding is that there is a desire to lift at least some parts of > this limitation eventually, whether that starts to happen in Swift 3 or is a > feature that comes later. > https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151214/002850.html > > <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151214/002850.html> Yes, there are plans to address this (and I’m looking forward to that!), but I think the difference is still going to be there and when to use one or the other is still going to remain a point of confusion. > One specific problem with requiring conformance is that forwarding to > existentials would not be straightforward because they do not conform to > their protocol. Obviously this is a limitation that should be lifted > eventually but it isn’t clear when that might happen. > >> >>> >>>> >>>>> >>>>>> >>>>>>> Another difference that is maybe subtle but I think important is that >>>>>>> with the approach I considered forwarding is declared in the body of a >>>>>>> type or extension which emphasizes the fact that forwarding is an >>>>>>> implementation detail, not something users of the type should be >>>>>>> concerned with. >>>>>> But what is the benefit of this emphasis? No solution requires to make >>>>>> the details visible in the public interface, and the ability to bury an >>>>>> important thing like protocol conformance somewhere in the class >>>>>> implementation is no advantage for me. >>>>> >>>>> Protocol conformance is not buried in the implementation in my solution. >>>>> I hope the previous example makes that clear. What is buried in the >>>>> implementation is the forwarding declaration which causes the compiler to >>>>> synthesize forwarding member implementations. This synthesis is an >>>>> implementation detail and should not be visible outside the >>>>> implementation. >>>>> >>>>>> >>>>>>> This approach was abandoned as it leads to problems in expressivity and >>>>>>> clarity. Please see alternatives considered for an elaboration of >>>>>>> that. This is especially true with the new approach to handling Self >>>>>>> values that Brent suggested. That approach requires additional syntax >>>>>>> around the forwarding declaration, but adds both clarity and >>>>>>> expressiveness. >>>>>> I think there is little need to worry about expressiveness for a feature >>>>>> that most potential users will probably never utilize in real code — and >>>>>> I don't think options like not conforming to a protocol that is >>>>>> forwarded is a big win here. It looks to me like you are optimizing for >>>>>> very uncommon cases, and sacrificing ease of use in the situations that >>>>>> are the most common by far. >>>>> >>>>> Like I stated, I am working on adding several examples of how this >>>>> feature can be used in real code. Please have a look at the lazy >>>>> collections example I shared last night. This example, as well as at >>>>> least one other coming examples take advantage of the ability to use >>>>> forwarding without requiring conformance. >>>>> >>>>> As with the memberwise initialization proposal, the syntax you would like >>>>> to see can easily be added as syntactic sugar on top of the current >>>>> proposal. I would not support that as I do not like the syntax Kotlin >>>>> uses for reasons already stated, but that shouldn’t stop you from >>>>> pursuing a proposal for it. Maybe a lot of people would agree with you >>>>> and it would be accepted. >>>>> >>>>> Matthew >>>>> >>>>>> >>>>>>> It appears to me that you value conciseness very highly. I do value >>>>>>> conciseness but I also value safety, clarity, and expressiveness. >>>>>> No, I value elegance and simplicity — they often lead to clarity and >>>>>> safety. >>>>>> >>>>>>>> Why not simply make this feature syntactic sugar and just >>>>>>>> auto-generate the forwarding methods? >>>>>>> >>>>>>> That is exactly what this proposal does. Why do you feel it is not >>>>>>> doing that? >>>>>> you're right, guess I mixed up the proposal with something else; so at >>>>>> least we agree on how it should work ;-) >>>>>> >>>>>> I'm not saying Swift has to copy another language, but I doubt that >>>>>> anyone who knows Kotlin would actually consider to drop their solution >>>>>> in favor of what is currently discussed... >>>>>> >>>>>> Tino >>>>>> >>>>> >>>>> _______________________________________________ >>>>> 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> >> >> -Dave -Dave
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution