…all i wanted to do was write Thing:Namespace.Protocol On Thu, Dec 28, 2017 at 4:43 PM, Adrian Zubarev via swift-evolution < [email protected]> wrote:
> Well again I don’t think we should disallow capturing the outer generic > type parameter just because you cannot use the protocol inside the outer > type atm., you still can add a type-eraser. To be honest such usage of the > existential is not even a requirement for the outer type. On there other > hand we might want to set the default for the associated type like I showed > in my previous message. The nested protocol could serve a completely > different purpose. Furthermore I still think that Generic<A>.P and > Generic<B>.P should be distinct protocols just like nested generic and > non-generic types are within an outer generic type. Sure there could be > other problems with ambiguity if you think of something like > GenericViewController<T>.Delegate, but the disambiguation when conforming > to such protocols requires a different solution and is a well known > limitation today. > > That said you won’t design such nested types anyways if you know the > existing language limitation. I’d say let’s keep it simple in theory and > just align the nesting behaviour. > > About existentials: > > For that scenario I can only speak for myself. I wouldn’t want to allow > directly the where clause existentials like this. It is far better and > more readable when we force the where clause on typealiases instead. We > could lift that restriction later if we’d like to, but not the other way > around. I think it’s okay if we start with a small restriction first and > see if it adopts well (this is MHO), because this way it shouldn’t harm > anybody. > > > > > Am 28. Dezember 2017 um 21:51:29, Karl Wagner ([email protected]) > schrieb: > > > > On 28. Dec 2017, at 12:34, Adrian Zubarev <[email protected]> > wrote: > > I disagree with some of your points. Do begin with, I don’t think we > should disallow capturing the generic type parameter, because I think there > might be a good way to prevent parameterization of nested protocols. > > To me this only feels like a natural consequence of nesting protocols > anyways. To achieve this we have to provide an explicit associated type > which will have a default type that refers to the captured outer generic > type parameter. At this point we discover another issue that we cannot > disambiguate generic type parameter like associated types yet and would be > forced to name the associated type of the protocol differently. > > struct Generic<T> { > protocol P { > associatedtype R = T > func f() -> R > } > } > > As you can see I didn’t include the variable in this example, because > existential are orthogonal this issue. David Hart and I still want to write > a proposal to allow the where clause on typealiases - maybe after the > forum officially launches. > > Above I said that there is an issue and provided an example that would > solve that issue with todays syntax, but I’d rather expand this idea. > Consider this syntax of a generic type and a protocol with an associated > type. > > protocol Proto { > associatedtype Element > } > > Proto.Element // This is an error like this, but it's still allowed in a > generic context > > func function<P : Proto>(_: P) where P.Element == Int {} > > protocol OtherProto : Proto where Element == Int {} > > struct Test<Element> {} > > extension Test where Element == Int {} > > Test.Element // Can/should we allow this? > > If we could allow this in Swift then the above example with the nested > protocol could be disambiguated nicely. > > struct Generic<T> { > protocol P { > associatedtype T = Generic.T > func f() -> T > } > } > > Remember that Generic.T is only the default for P.T if you don’t set it > yourself but when you conform or use that that protocol (in a generic > context) you can still set it differntly. > > This consequence disallows protocol parameterization through nesting in a > generic types, but still behaves very similar to nested generic types: > > struct Test<T> { > struct NonGeneric { > var t: T > } > > struct Generic<R> { > var t: T > var r: R > } > } > > _ = Test<String>.NonGeneric(t: "😎") > _ = Test<String>.Generic<Int>(t: "🤓", r: 42) > > —— > > > Yeah, that’s all well and good. I don’t think we should parameterise > protocols either; it feels like Swift hasn’t been designed with those in > mind, and that’s fine. You would get in to all kinds of horrible conflicts > if you tried to conform to both Generic<Int>.P and Generic<String>.P, > because most functions would have the same signature but possibly very > different implementations. You would likely end up having to separate the > conformances by using a wrapper struct — in which case, why not just make > them the same protocol and have the existing duplicate-conformance rules > take care of it? > > An earlier version of the proposal included something like you describe. > Basically, Generic<Int>.P and Generic<String>.P would be the same protocol. > They would have an associated type to represent the parameter from > Generic<T>, and within Generic<T>, all references to P would be implicitly > constrained so that P.T == Self.T. You would write conformances to > “Generic.P” with a constraint for T, as you do today. > > And for the existential variable inside Genric it really should be > something like this (when the where clause is allowed and if we can refer > differently to generic type parameters as well): > > struct Generic<T> { > … > typealias PConstrainedByT = P where T == Self.T > var object: PConstrainedByT > } > > > If we have that ability, then we can totally do capturing. Forgive me, but > I understand that as pretty-much the same as generalised existentials > (without local type binding). > If I can write the type of object as an existential of (generic protocol + > constraints) via a typealias, then surely I must also be able to do it > directly? So I could also write: > > struct Generic<T> { > var object: P where T == Self.T > } > > Anyway, I thought that was not on the table, and in any case I’m convinced > that it should be a separate proposal. This gets to the heart of the > interaction between generic types and protocols, and we all know it’s not > always a smooth transition (hello AnyCollection, AnyHashable, etc...). We > can cover the common cases (i.e. the Apple frameworks) without requiring > capturing - especially since it’s apparently not too difficult to implement > - and build from there. > > - Karl > > > > > Am 27. Dezember 2017 um 19:53:36, Karl Wagner via swift-evolution ( > [email protected]) schrieb: > > Yeah I wrote that proposal. I eventually stripped it down to just disallow > all capturing, but it was still not selected by the core team for review > ¯\_(ツ)_/¯ > > As for capturing semantics, once you start working through use-cases, it’s > becomes clear that it's going to require generalised existentials. > Otherwise, how would you use a Generic<T>.P? > > struct Generic<T> { > protocol P { func f() -> T } > > var object: P // uh-oh! ‘Generic protocol can only be used as a > generic parameter constraint' > } > > So, you would need to add a generic parameter to actually *use* P from > within Generic<T>, which of course limits you to a single concrete type of > P: > > struct Generic<T, TypeOfP> where TypeOfP: Self.P { // Could this even > work? What if P captures TypeOfP? > protocol P { /* … */ } > var object: TypeOfP > } > > Which is just yucky. > > Ideally, the type of ‘object’ should be ‘Any<P where P.T == T>’, to > express that it can be any conforming type with the appropriate > constraints. You wouldn’t need to write that all out; we could infer that > capturing is equivalent to a same-type constraint (or perhaps one of these > “generalised supertype constraints” that were pitched recently). But we > can’t express those kinds of existentials everywhere in the type-system > today, so most examples of capturing fall down pretty quickly. > > - Karl > > On 25. Dec 2017, at 03:56, Slava Pestov via swift-evolution < > [email protected]> wrote: > > There was a proposal to allow protocols to be nested inside types at one > point but it didn’t move forward. > > Basically, if the outer type is a non-generic class, struct or enum, > there’s no conceptual difficulty at all. > > If the outer type is a generic type or another protocol, you have a > problem where the inner protocol can reference generic parameters or > associated types of the outer type. This would either have to be banned, or > we would need to come up with coherent semantics for it: > > struct Generic<T> { > protocol P { > func f() -> T > } > } > > struct Conforms : Generic<Int>.P { > func f() -> Int { … } // Like this? > } > > let c = Conforms() > c is Generic<String>.P // is this false? Ie, are Generic<Int>.P and > Generic<String>.P different protocols? > > Slava > > On Dec 24, 2017, at 6:53 PM, Kelvin Ma via swift-evolution < > [email protected]> wrote: > > is there a reason why it’s not allowed to nest a protocol declaration > inside another type? > _______________________________________________ > swift-evolution mailing list > [email protected] > https://lists.swift.org/mailman/listinfo/swift-evolution > > > _______________________________________________ > swift-evolution mailing list > [email protected] > https://lists.swift.org/mailman/listinfo/swift-evolution > > > _______________________________________________ > swift-evolution mailing list > [email protected] > https://lists.swift.org/mailman/listinfo/swift-evolution > > > > _______________________________________________ > swift-evolution mailing list > [email protected] > https://lists.swift.org/mailman/listinfo/swift-evolution > >
_______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
