Namespacing protocols to other types

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 (razielim@gmail.com) schrieb:

···

On 28. Dec 2017, at 12:34, Adrian Zubarev <adrian.zubarev@devandartist.com> 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: ":sunglasses:")
_ = Test<String>.Generic<Int>(t: ":nerd_face:", 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 (swift-evolution@swift.org) 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&#39;

}

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

is there a reason why it’s not allowed to nest a protocol declaration inside another type?
_______________________________________________
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

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

…all i wanted to do was write Thing:Namespace.Protocol

···

On Thu, Dec 28, 2017 at 4:43 PM, Adrian Zubarev via swift-evolution < swift-evolution@swift.org> 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 (razielim@gmail.com)
schrieb:

On 28. Dec 2017, at 12:34, Adrian Zubarev <adrian.zubarev@devandartist.com> > 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: ":sunglasses:")
_ = Test<String>.Generic<Int>(t: ":nerd_face:", 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 (
swift-evolution@swift.org) 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 < > swift-evolution@swift.org> 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 < > swift-evolution@swift.org> wrote:

is there a reason why it’s not allowed to nest a protocol declaration
inside another type?
_______________________________________________
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

_______________________________________________
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

Modules in Swift are really designed for code that is intended to be
shipped separately. as Jon found out modules are pretty heavyweight and
introduce a lot of unwanted abstraction and complexity when all you want to
do is write things in a namespace. (Also if you forgot, importing a module
in Swift dumps all of its symbols into the global namespace so Module.Thing
is really meaningless.) sometimes this is a good thing because no one wants
to be writing Glibc.fopen over and over especially when you have to #if
#endif it out every time with Darwin.fopen if you want your library to run
on OSX but this feature also makes modules ineffective as a namespacing
scheme.

···

On Fri, Dec 29, 2017 at 8:35 PM, Eneko Alonso <eneko.alonso@gmail.com> wrote:

…all i wanted to do was write Thing:Namespace.Protocol

Have you thought of using Swift modules for that? Maybe that would be a
better approach than trying to name-space within a module?

Regards,
Eneko Alonso

On Dec 29, 2017, at 16:51, Kelvin Ma via swift-evolution < > swift-evolution@swift.org> wrote:

…all i wanted to do was write Thing:Namespace.Protocol

On Thu, Dec 28, 2017 at 4:43 PM, Adrian Zubarev via swift-evolution < > swift-evolution@swift.org> 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 (razielim@gmail.com)
schrieb:

On 28. Dec 2017, at 12:34, Adrian Zubarev <adrian.zubarev@devandartist.c >> > 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: ":sunglasses:")
_ = Test<String>.Generic<Int>(t: ":nerd_face:", 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 (
swift-evolution@swift.org) 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 < >> swift-evolution@swift.org> 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 < >> swift-evolution@swift.org> wrote:

is there a reason why it’s not allowed to nest a protocol declaration
inside another type?
_______________________________________________
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

_______________________________________________
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

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

Modules do more than that. For instance, if two imported modules provide a type with the same name, you can distinguish (name-spacing) them by using ModuleA.Thing vs ModuleB.Thing.

In regards of Modules being intended to be shipped separately, that is incorrect. You might be thinking of packages.

In Swift 4, a package can contain multiple binaries, including executables and libraries. These, can be composed of one or many modules.

Swift Package Manager handles this really well, and allows to organize the code separating it by features or concerns.

I understand Xcode does not have very good support for modules yet, other than importing frameworks. But it is my understanding this is something the new Xcode’s build system will solve, hopefully by Xcode 10.

I’m not against nesting protocol definitions, I can see a few cases where it could be nice to have them. But in regards of namespacing, I think using nested types is the wrong direction.

Thank you,
Eneko Alonso

···

On Dec 29, 2017, at 19:00, Kelvin Ma <kelvin13ma@gmail.com> wrote:

Modules in Swift are really designed for code that is intended to be shipped separately. as Jon found out modules are pretty heavyweight and introduce a lot of unwanted abstraction and complexity when all you want to do is write things in a namespace. (Also if you forgot, importing a module in Swift dumps all of its symbols into the global namespace so Module.Thing is really meaningless.) sometimes this is a good thing because no one wants to be writing Glibc.fopen over and over especially when you have to #if #endif it out every time with Darwin.fopen if you want your library to run on OSX but this feature also makes modules ineffective as a namespacing scheme.

On Fri, Dec 29, 2017 at 8:35 PM, Eneko Alonso <eneko.alonso@gmail.com> wrote:

…all i wanted to do was write Thing:Namespace.Protocol

Have you thought of using Swift modules for that? Maybe that would be a better approach than trying to name-space within a module?

Regards,
Eneko Alonso

On Dec 29, 2017, at 16:51, Kelvin Ma via swift-evolution <swift-evolution@swift.org> wrote:

…all i wanted to do was write Thing:Namespace.Protocol

On Thu, Dec 28, 2017 at 4:43 PM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> 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 (razielim@gmail.com) schrieb:

On 28. Dec 2017, at 12:34, Adrian Zubarev <adrian.zubarev@devandartist.com> 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: ":sunglasses:")
_ = Test<String>.Generic<Int>(t: ":nerd_face:", 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 (swift-evolution@swift.org) 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 <swift-evolution@swift.org> 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 <swift-evolution@swift.org> wrote:

is there a reason why it’s not allowed to nest a protocol declaration inside another type?
_______________________________________________
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

_______________________________________________
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

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

Modules do more than that. For instance, if two imported modules provide a
type with the same name, you can distinguish (name-spacing) them by using
ModuleA.Thing vs ModuleB.Thing.

In regards of Modules being intended to be shipped separately, that is
incorrect. You might be thinking of packages.

In Swift 4, a package can contain multiple binaries, including executables
and libraries. These, can be composed of one or many modules.

Swift Package Manager handles this really well, and allows to organize the
code separating it by features or concerns.

the Swift language itself has no concept of packages. packages are a thing
that the SPM creates and manages and downloads for you. the modules inside
a package actually have internal dependencies between each other which is
why you have to write them all out in Package.swift. but you can distribute
a package with modues that are decoupled from one another and another app
or library can import that one module and not all the others (though the
SPM will still download all of them for some reason.)

I understand Xcode does not have very good support for modules yet, other
than importing frameworks. But it is my understanding this is something the
new Xcode’s build system will solve, hopefully by Xcode 10.

I’m not against nesting protocol definitions, I can see a few cases where
it could be nice to have them. But in regards of namespacing, I think using
nested types is the wrong direction.

the Swift standard library (the actual stdlib not Foundation) actually uses
this system like Unicode
<https://developer.apple.com/documentation/swift/unicode&gt; and CommandLine
<https://developer.apple.com/documentation/swift/commandline&gt;\.

···

On Fri, Dec 29, 2017 at 11:20 PM, Eneko Alonso <eneko.alonso@gmail.com> wrote:

Thank you,
Eneko Alonso

On Dec 29, 2017, at 19:00, Kelvin Ma <kelvin13ma@gmail.com> wrote:

Modules in Swift are really designed for code that is intended to be
shipped separately. as Jon found out modules are pretty heavyweight and
introduce a lot of unwanted abstraction and complexity when all you want to
do is write things in a namespace. (Also if you forgot, importing a module
in Swift dumps all of its symbols into the global namespace so
Module.Thing is really meaningless.) sometimes this is a good thing
because no one wants to be writing Glibc.fopen over and over especially
when you have to #if #endif it out every time with Darwin.fopen if you
want your library to run on OSX but this feature also makes modules
ineffective as a namespacing scheme.

On Fri, Dec 29, 2017 at 8:35 PM, Eneko Alonso <eneko.alonso@gmail.com> > wrote:

…all i wanted to do was write Thing:Namespace.Protocol

Have you thought of using Swift modules for that? Maybe that would be a
better approach than trying to name-space within a module?

Regards,
Eneko Alonso

On Dec 29, 2017, at 16:51, Kelvin Ma via swift-evolution < >> swift-evolution@swift.org> wrote:

…all i wanted to do was write Thing:Namespace.Protocol

On Thu, Dec 28, 2017 at 4:43 PM, Adrian Zubarev via swift-evolution < >> swift-evolution@swift.org> 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 (razielim@gmail.com)
schrieb:

On 28. Dec 2017, at 12:34, Adrian Zubarev <adrian.zubarev@devandartist.c >>> > 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: ":sunglasses:")
_ = Test<String>.Generic<Int>(t: ":nerd_face:", 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 (
swift-evolution@swift.org) 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 < >>> swift-evolution@swift.org> 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 < >>> swift-evolution@swift.org> wrote:

is there a reason why it’s not allowed to nest a protocol declaration
inside another type?
_______________________________________________
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

_______________________________________________
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

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

since everyone is reviving their dead threads, anyone down to revisit this?

I do miss nested interfaces in Java, which is great for scoping delegates. Here's something I'd like to do in Swift:

class FileInformationProvider {
  protocol Delegate: class { }

  weak var delegate: Delegate?
}

class FileManager: FileInformationProvider.Delegate { }

Nesting protocols could really benefit for the same reasons of nesting classes, structs, and enums. Protocols shouldn't pollute the global namespace when only relevant to a certain type like delegates.

13 Likes

I have an implementation that I worked on a while ago that allows you to nest a protocol inside a class, struct or enum (but not a protocol). However, it won't allow you to capture the parent type's generic context. So, it's meant to be used purely for name spacing purposes, which I believe is what 99.9% of the people want. The generic stuff can come later, perhaps when we have generic protocols.

I kept the implementation simple so we can focus on what most people need and not have to worry about other complicated stuff like capturing generic parameters or associated types of the parent type and other issues.

I can write a proposal for it and pitch it.

7 Likes

Please do! :clap: Does it allow nesting other types in protocols (if the protocol does not use Self or associated types)?

No, it only allows you to nest a protocol inside non-generic classes, structs or enums. It doesn't allow you to do the reverse. I don't think it should be allowed personally because everything inside a protocol (except typealiases) are requirements, however we could allow it to be declared inside a protocol extension instead.

protocol A {
  enum B {} // error
}

extension A {
  enum B {} // ok
}
1 Like

It‘s kind of the same thing, I‘m fine with allowing nested types in protocol extensions but the protocol should support these types im protocol requirements as well.

protocol P {
  var b: B { get }
}

extension P {
  struct B {}
}

I don't think this makes sense. Protocol extensions extend all conforming types in addition to the existential. See the recent discussion about nesting and namespacing in the Improving the UI of Generics thread for more of my thoughts on this topic.

1 Like

What does not make sense to you?

To me it‘s the same as if you would write typealias B = P_B where P_B is a pseudo nested type but declared globally as nesting is currently not allowed. Right now you would only be allowed to do so if the protocol itself does not use Self or associated types. Is there anything I am missing?

struct P_B {}
protocol P {
  // can live inside extension as well
  typealias B = P_B
}

protocol Q {}
extension Q {
  struct A {}
}

struct S: P, Q {}

S.B.self == P.B.self // == P_B.self
S.A.self == Q.A.self

Yes I have a PR for this: Allow protocols to be nested in non-generic contexts by karwa · Pull Request #23096 · apple/swift · GitHub

I got fed up pinging people every week and left it. Personally I think it's obvious enough that it doesn't need to go through swift-evolution.

You can't inject a type declaration into multiple parent declarations. That doesn't make sense. It makes sense for typealias because they are transparent and evaluate to the specified type. If what you suggest were allowed the implementation would need to actually inject a typealias into the conforming types. I would rather not allow that. It would be better if this was done explicitly when desired.

Why would you not want conforming types inherit the nested type? This makes perfect sense to me as it‘s what you want if you already writing type aliases. If the nested type also would capture the associated type (in the future) you‘d want the nested type to be inherited by the conforming type as it will provide a concrete type for the protocols associated type.

This is the natural behavior I would expect from nested protocol types. You can still use them directly if they don‘t capture anything. I don‘t see any disadvantages. Do you have any examples in mind that would be harmful if we did it that way?

We already have this behavior on classes.

class X {
  enum E {}
}
class Y: X {}

X.E.self == Y.E.self

That’s great! No need for me to push my implementation then.

The reason I often want to nest a type inside a protocol is because it is a helper type for the API of the protocol. I do not want these helper types to be added as a nested type of every conforming type. If you want that typealias it’s not a big deal to have to write it manually.

How would you do it? You already can‘t avoid it on classes and a type alias in protocols is basically a nested type. If you had to do it on protocols it would require you to come up with a different type name yet again as typealias B = B would not make sense to the compiler. I think you expect a non natural behavior here. (If I‘m the one who has it wrong, please feel free to correct me.)

A type nested inside a generic type can be "lifted" to a top-level type whose generic arguments and requirements are the concatenation of the outer type and inner type. Eg,

struct Outer<T> where T : Equatable {
  struct Inner<U> where U : Sequence, U.Element == T { ... }
}

is the same as this:

struct OuterInner<T, U> where T : Equatable, U : Sequence, U.Element == T { ... }

Now suppose you have a type like this:

protocol Outer {
  struct Inner<U> where U : Sequence, U.Element == Self { ... }
}

Since a protocol Proto has a single generic parameter Self with a single requrement Self : Proto, this is in fact equivalent to writing:

struct ProtocolInner<Self, U> where Self : Outer, U : Sequence, U.Element == Self { ... }

The only difference is that for some type C conforming to Outer, I write C.Inner<Foo> instead of writing ProtocolInner<C, Foo>.

This also means that referencing the nested type as a member of the protocol itself is not valid! Eg, Outer.Inner<Foo> is not valid because Outer does not conform to itself. Even if the generic requirements of Inner do not reference Self or associated types, the bodies of its methods (or methods defined in extensions) might, so in general we can only allow types nested inside protocols to be seen as members of conforming types, and not of the existential type.

Note that all of the above applies equally well to nested type aliases as well as nested nominal types. We already worked out most of the edge cases for nested type aliases so allowing nominals to be nested inside protocols won't be a big step.

Edit: This is about struct/enum/class declarations nested inside protocols. Protocols nested inside other protocols runs afoul of the "protocol cannot be in generic context" rule, which is more difficult to resolve. Right now we assume a protocol only has a single generic parameter Self, and there are multiple independent ways of generalizing this, all of them rather difficult.

8 Likes