Nested types in protocols (and nesting protocols in types)

There are a few more things I forget to mention in my previous post.

Consider these examples:

public protocol A {
    protocol B {}
}

// means A.B is public

extension A {
    protocol C {}
}

// means `A.C` is internal
Without extensions this wouldn’t be possible. This flexibility must exist, because we don’t allow access modifier inside protocols.

Plus it’s interesting to see what we could build with constrained extensions.

protocol Proto {}

extension Proto where Self : SomethingElse {
     
      class Inner {}
}
And the last but not least. I already started a topic about the inconsistency of public/open access modifier to protocols. I my world I think this should be possible:

public class Something {
     
    open protocol Delegate { … }
     
    public protocol Interface { … }
}
In a different module it’s possible to use the Something class but you cannot subclass it.

We should be able to conform in a different module to Something.Delegate.

We cannot conform to Something.Interface, but can use it as an interface for a specific purpose.

···

--
Adrian Zubarev
Sent with Airmail

Am 22. Oktober 2016 um 10:14:17, Adrian Zubarev (adrian.zubarev@devandartist.com) schrieb:

First of all, thank you for writing the formal proposal.

To begin with, I’d like to remember you that type nesting is not only done through nesting types directly in concrete types. A commonly used pattern to reduce clustering is nesting inside extensions:

extension TypeName {
      
    class InnerTypeName { … }
}
Next up:

protocol Outer {
     protocol Inner {
         associatedtype Huh // why would you do this?
     }
}
I disagree with this: why would you want to restrict this?

The Outer protocol might be simple by the inner protocol is not. If you’d move the associatedtype Huh into Outer protocol you’d automatically break the intended simplicity of the Outer protocol.

The following example is kept simple just to show that the outer protocol is in our case intended to be simple where the inner is not.

protocol Building {
      
    protocol Room {

        associatedtype Person
    }

    var numberOfRooms: Int { get }
}

struct Pupil {}
struct Student {}

struct University : Building {
      
    let numberOfRooms: Int = 1000

    struct Room : Building.Room {

        typealias Person = Student
    }
}

struct School : Building {
      
    let numberOfRooms: Int = 100

    struct Room : Building.Room {

        typealias Person = Pupil
    }
}

let buildings: [Building] = [University(), School()] // fine because `Building` is simple
The inner types in this example are different from each other, but this wasn’t the main point anyways.

Unless this restriction would have other technical reasons than “It is hard to think of a valid reason why a nested protocol might want to be more generic than its parent”, I’m against it.

At the very beginning I said that nesting is also done through extensions. Let’s rewrite the above example from your draft that way and see what we good.

protocol Outer {} // Simple

extension Outer {
      
    // Complex
    protocol Inner {
     associatedtype Huh // why would you do this?
  }
}

// new name spacing `Outer.Inner`
From this perspective it’s much clearer now that the Outer protocol type in out case is intended to be simple.

About associatedtypes with extensions:

In Swift 3.0 this code compiles:

public class Class {}

public protocol Outer {
    associatedtype AAA : Class
}

extension Outer {
    public typealias AAA = Int
}
This does mean that the associatedtype is lost in extensions and can be reused as an inner type for nesting. This also means that the inner type wouldn’t be able to capture the associatedtype from the outer type and might be simple.

protocol Outer {

   associatedtype AAA
}

extension Outer {
    protocol Inner {} // Simple because nesting from within extensions seems to ignore the associatedtype from the `Outer` type
}
You can compare this to todays workarounds where you only want to create a nice name space but want to keep the inner type simple.

// Implementation artifact
protocol _Inner {} // simple

// Complex
protocol Outer {
    associatedtype Anything
}

extension Outer {
    typealias Inner = _Inner
}

// Free to use `Outer.Inner`

--
Adrian Zubarev
Sent with Airmail

Am 22. Oktober 2016 um 04:18:25, Karl (razielim@gmail.com) schrieb:

On 22 Oct 2016, at 04:12, Karl <raziel.im+swift-evo@gmail.com> wrote:

On 22 Oct 2016, at 04:07, Karl <raziel.im+swift-evo@gmail.com> wrote:

On 22 Oct 2016, at 04:02, Braeden Profile <jhaezhyr12@gmail.com> wrote:

But what would that mean? If I reference `ProtocolName.InnerType`, that doesn’t always have meaning. In fact, if you have two different extensions where AssociatedType equals something else, there’s a type ambiguity from other code. I suspect it would only work if that InnerType was mandated to be `private`.

You would need a reference to a (ProtocolName where AssociatedType == Int), which you can get either from a `self` inside the extension or from a generic parameter:

struct MyValue<T> : ProtocolName { typealias AssociatedType = T }

let _ = MyValue<Int>().InnerType()

No, wait - sorry, that’s wrong. I got confused for a second. You’re right; it would have to be a private type.

Actually I think I take that back (I was just writing, lots of snippets floating around my head) - ProtocolName is a generic protocol, so types inside of it would become types on the concrete conformers. That’s consistent with the Editor.Delegate example in the draft proposal I linked to.

So MyValue<Int>.InnerType would exist :+1: ProtocolName.InnerType isn’t really very meaningful otherwise.

- Karl

On Oct 17, 2016, at 12:44 PM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

That option should not be disallowed. Here is a simple example you might want to build at some point:

protocol ProtocolName {
       
    associatedtype AssociatedType
}

extension ProtocolName where AssociatedType == Int {
    
    struct InnerType {}
}

--
Adrian Zubarev
Sent with Airmail

Am 17. Oktober 2016 um 20:30:58, Karl via swift-evolution (swift-evolution@swift.org) schrieb:

Is your vision that each conforming type would have to provide its own nested type as specified by the protocol?

Or could the protocol itself define a nested type and anything could use it?

protocol FloatingPoint: … {
enum RoundingRule {
// Do I put an implementation here?
}
}

No, types which are defined inside the protocol are implemented there. Providing your own types to satisfy a conformance is what associated types are for.

If you wanted something like that, you could do it with a nested protocol + associated type:

protocol FloatingPoint {

protocol \_RoundingRule \{ func round\(\_ : Super\) \-&gt; Super \}
associatedType RoundingRule : \_RoundingRule

}

struct Float : FloatingPoint {

enum RoundingRule : \_RoundingRule \{
    func round\(\_ val: Float\) \-&gt; Float \{
        /\* switch self, perform rounding… \*/ 
    \}
\}

}

That brings up an interesting point, though - we would need a way to refer to the outer protocol (I used “Super” here).

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

1 Like

However protocols nested inside types and types nested inside protocols is still not supported, because protocols introduce a separate series of issues involving associated types and the ’Self’ type.

The hard part of getting nested generics right is what to do if a nested type ‘captures’ generic parameters of the outer type. For non-protocol types, the behavior here is pretty straightforward.

If we allow protocols to be nested inside other types, we have to decide what to do if the protocol ‘closes over’ generic parameters of the outer type. For example,

struct A<T> {
protocol P {
func requirement() -> T
}
}

Presumably A<Int>.P and A<String>.P are distinct types, and A.P has a hidden associated type corresponding to the type parameter ’T’?

The other case is problematic too — the nested type might refer to an associated type of the outer protocol:

protocol P {
associatedtype A

struct T {
var value: A
}
}

Now writing P.T does not make sense, for the same reason that we cannot form an existential of type P.A. We could prohibit references to outer associated types of this form, or we could figure out some way to give it a meaning. If C is a concrete type conforming to P, then certainly C.T makes sense, for instance. Internally, the nested type A.T could have a hidden ‘Self’ generic type parameter, so that writing C.T is really the same as P.T<C>.

Protocols nested inside protocols also have the same issue.

FWIW, in almost all the situations where I’ve wanted to nest types inside protocols and generic types, it’s only as a namespacing convenience. Most often, it’s an enum type that’s used only by a single method, and having it at the top of the module namespace adds clutter.

Here’s a real life example pared down. I wish I could do this:

public struct ResponseContentTransformer<InputContentType, OutputContentType>: ResponseTransformer {

   public init(onInputTypeMismatch mismatchAction: InputTypeMismatchAction = .error) {
     ...
   }

   public enum InputTypeMismatchAction { // Does not depend on generic types above
     case error
     case skip
     case skipIfOutputTypeMatches
   }

}

InputTypeMismatchAction is tightly associated with ResponseContentTransformer, and is confusing as a top-level type.

What do you think about providing a “no captures” modifier for nested types — like static inner classes in Java? Then Swift could provide the namespace nesting I wish for this without having to resolve the trickier type capture questions yet.

Alternatively, what if (1) outer types aren’t capture unless they’re referenced, and (2) nesting is only illegal if there’s a capture? Then my code above would compile, as would this:

public struct S<T> {
   public enum Foo {
     case yin
     case yang
   }
}

…but this wouldn’t:

public struct S<T> {
   public enum Foo {
     case yin(thing: T) // capture of T illegal (for now)
     case yang
   }
}

Either of these approaches would allow hygienic namespacing now while leaving the door open to outer type capture in the future.

Yeah, this makes sense for a first cut at this feature.

Slava

Should I take a crack at writing up a proposal for this? Now? After ABI work is done? (Probably the latter “OK if no captures” approach?) Eager to help; don’t want to be in the way.

Just speaking for myself and not the whole team — I think you can submit the proposal at any time, we’re unlikely to get around to doing it, if you want to take a crack that would be great (again, with ‘no captures’ it’s “trivial”).

Slava

···

On Nov 2, 2016, at 8:32 AM, Paul Cantrell <cantrell@pobox.com> wrote:

On Oct 24, 2016, at 4:43 PM, Slava Pestov <spestov@apple.com> wrote:

On Oct 24, 2016, at 8:12 AM, Paul Cantrell <cantrell@pobox.com> wrote:

On Oct 24, 2016, at 5:09 AM, Slava Pestov via swift-evolution <swift-evolution@swift.org> wrote:

P

Sorry, let this slip. Proposal sent - https://github.com/apple/swift-evolution/pull/552

- Karl

···

On 2 Nov 2016, at 20:54, Slava Pestov <spestov@apple.com> wrote:

On Nov 2, 2016, at 8:32 AM, Paul Cantrell <cantrell@pobox.com> wrote:

On Oct 24, 2016, at 4:43 PM, Slava Pestov <spestov@apple.com> wrote:

On Oct 24, 2016, at 8:12 AM, Paul Cantrell <cantrell@pobox.com> wrote:

On Oct 24, 2016, at 5:09 AM, Slava Pestov via swift-evolution <swift-evolution@swift.org> wrote:

However protocols nested inside types and types nested inside protocols is still not supported, because protocols introduce a separate series of issues involving associated types and the ’Self’ type.

The hard part of getting nested generics right is what to do if a nested type ‘captures’ generic parameters of the outer type. For non-protocol types, the behavior here is pretty straightforward.

If we allow protocols to be nested inside other types, we have to decide what to do if the protocol ‘closes over’ generic parameters of the outer type. For example,

struct A<T> {
protocol P {
func requirement() -> T
}
}

Presumably A<Int>.P and A<String>.P are distinct types, and A.P has a hidden associated type corresponding to the type parameter ’T’?

The other case is problematic too — the nested type might refer to an associated type of the outer protocol:

protocol P {
associatedtype A

struct T {
var value: A
}
}

Now writing P.T does not make sense, for the same reason that we cannot form an existential of type P.A. We could prohibit references to outer associated types of this form, or we could figure out some way to give it a meaning. If C is a concrete type conforming to P, then certainly C.T makes sense, for instance. Internally, the nested type A.T could have a hidden ‘Self’ generic type parameter, so that writing C.T is really the same as P.T<C>.

Protocols nested inside protocols also have the same issue.

FWIW, in almost all the situations where I’ve wanted to nest types inside protocols and generic types, it’s only as a namespacing convenience. Most often, it’s an enum type that’s used only by a single method, and having it at the top of the module namespace adds clutter.

Here’s a real life example pared down. I wish I could do this:

public struct ResponseContentTransformer<InputContentType, OutputContentType>: ResponseTransformer {

  public init(onInputTypeMismatch mismatchAction: InputTypeMismatchAction = .error) {
    ...
  }

  public enum InputTypeMismatchAction { // Does not depend on generic types above
    case error
    case skip
    case skipIfOutputTypeMatches
  }

}

InputTypeMismatchAction is tightly associated with ResponseContentTransformer, and is confusing as a top-level type.

What do you think about providing a “no captures” modifier for nested types — like static inner classes in Java? Then Swift could provide the namespace nesting I wish for this without having to resolve the trickier type capture questions yet.

Alternatively, what if (1) outer types aren’t capture unless they’re referenced, and (2) nesting is only illegal if there’s a capture? Then my code above would compile, as would this:

public struct S<T> {
  public enum Foo {
    case yin
    case yang
  }
}

…but this wouldn’t:

public struct S<T> {
  public enum Foo {
    case yin(thing: T) // capture of T illegal (for now)
    case yang
  }
}

Either of these approaches would allow hygienic namespacing now while leaving the door open to outer type capture in the future.

Yeah, this makes sense for a first cut at this feature.

Slava

Should I take a crack at writing up a proposal for this? Now? After ABI work is done? (Probably the latter “OK if no captures” approach?) Eager to help; don’t want to be in the way.

Just speaking for myself and not the whole team — I think you can submit the proposal at any time, we’re unlikely to get around to doing it, if you want to take a crack that would be great (again, with ‘no captures’ it’s “trivial”).

Slava

P

1 Like

Disappointed to see that you just ignored everything I pitched to you. The proposal does not cover extensions nor it it sees the problem with access modifier which it creates.

public protocol A {
     
    // Not allowed to use any access modifier here at all
    struct B {
        // Nor here
        var something: C = …
    }
}
Everything will be public here. Try to build a true singleton like this for example.

···

--
Adrian Zubarev
Sent with Airmail

Am 5. November 2016 um 10:44:27, Karl via swift-evolution (swift-evolution@swift.org) schrieb:

On 2 Nov 2016, at 20:54, Slava Pestov <spestov@apple.com> wrote:

On Nov 2, 2016, at 8:32 AM, Paul Cantrell <cantrell@pobox.com> wrote:

On Oct 24, 2016, at 4:43 PM, Slava Pestov <spestov@apple.com> wrote:

On Oct 24, 2016, at 8:12 AM, Paul Cantrell <cantrell@pobox.com> wrote:

On Oct 24, 2016, at 5:09 AM, Slava Pestov via swift-evolution <swift-evolution@swift.org> wrote:

However protocols nested inside types and types nested inside protocols is still not supported, because protocols introduce a separate series of issues involving associated types and the ’Self’ type.

The hard part of getting nested generics right is what to do if a nested type ‘captures’ generic parameters of the outer type. For non-protocol types, the behavior here is pretty straightforward.

If we allow protocols to be nested inside other types, we have to decide what to do if the protocol ‘closes over’ generic parameters of the outer type. For example,

struct A<T> {
protocol P {
func requirement() -> T
}
}

Presumably A<Int>.P and A<String>.P are distinct types, and A.P has a hidden associated type corresponding to the type parameter ’T’?

The other case is problematic too — the nested type might refer to an associated type of the outer protocol:

protocol P {
associatedtype A

struct T {
var value: A
}
}

Now writing P.T does not make sense, for the same reason that we cannot form an existential of type P.A. We could prohibit references to outer associated types of this form, or we could figure out some way to give it a meaning. If C is a concrete type conforming to P, then certainly C.T makes sense, for instance. Internally, the nested type A.T could have a hidden ‘Self’ generic type parameter, so that writing C.T is really the same as P.T<C>.

Protocols nested inside protocols also have the same issue.

FWIW, in almost all the situations where I’ve wanted to nest types inside protocols and generic types, it’s only as a namespacing convenience. Most often, it’s an enum type that’s used only by a single method, and having it at the top of the module namespace adds clutter.

Here’s a real life example pared down. I wish I could do this:

public struct ResponseContentTransformer<InputContentType, OutputContentType>: ResponseTransformer {

public init(onInputTypeMismatch mismatchAction: InputTypeMismatchAction = .error) {
...
}

public enum InputTypeMismatchAction { // Does not depend on generic types above
case error
case skip
case skipIfOutputTypeMatches
}

}

InputTypeMismatchAction is tightly associated with ResponseContentTransformer, and is confusing as a top-level type.

What do you think about providing a “no captures” modifier for nested types — like static inner classes in Java? Then Swift could provide the namespace nesting I wish for this without having to resolve the trickier type capture questions yet.

Alternatively, what if (1) outer types aren’t capture unless they’re referenced, and (2) nesting is only illegal if there’s a capture? Then my code above would compile, as would this:

public struct S<T> {
public enum Foo {
case yin
case yang
}
}

…but this wouldn’t:

public struct S<T> {
public enum Foo {
case yin(thing: T) // capture of T illegal (for now)
case yang
}
}

Either of these approaches would allow hygienic namespacing now while leaving the door open to outer type capture in the future.

Yeah, this makes sense for a first cut at this feature.

Slava

Should I take a crack at writing up a proposal for this? Now? After ABI work is done? (Probably the latter “OK if no captures” approach?) Eager to help; don’t want to be in the way.

Just speaking for myself and not the whole team — I think you can submit the proposal at any time, we’re unlikely to get around to doing it, if you want to take a crack that would be great (again, with ‘no captures’ it’s “trivial”).

Slava

P

Sorry, let this slip. Proposal sent - Ease restrictions on protocol nesting by karwa · Pull Request #552 · apple/swift-evolution · GitHub

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

(Coming back to this after a very long time)

One very high-level comment: one of your early examples is making the Delegate of a view. Did you consider making this an Objective-C translation rule as well, so that *Delegate and *DataSource protocols would be imported as nested types within a class with the name signified by *, e.g.,

  class UITableView {
    @objc(UITableViewDataSource)
    protocol DataSource { … }

    @objc(UITableViewDelegate)
    protocol Delegate { … }
  }

  - Doug

···

On Nov 5, 2016, at 2:44 AM, Karl via swift-evolution <swift-evolution@swift.org> wrote:

On 2 Nov 2016, at 20:54, Slava Pestov <spestov@apple.com <mailto:spestov@apple.com>> wrote:

On Nov 2, 2016, at 8:32 AM, Paul Cantrell <cantrell@pobox.com <mailto:cantrell@pobox.com>> wrote:

On Oct 24, 2016, at 4:43 PM, Slava Pestov <spestov@apple.com <mailto:spestov@apple.com>> wrote:

On Oct 24, 2016, at 8:12 AM, Paul Cantrell <cantrell@pobox.com <mailto:cantrell@pobox.com>> wrote:

On Oct 24, 2016, at 5:09 AM, Slava Pestov via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

However protocols nested inside types and types nested inside protocols is still not supported, because protocols introduce a separate series of issues involving associated types and the ’Self’ type.

The hard part of getting nested generics right is what to do if a nested type ‘captures’ generic parameters of the outer type. For non-protocol types, the behavior here is pretty straightforward.

If we allow protocols to be nested inside other types, we have to decide what to do if the protocol ‘closes over’ generic parameters of the outer type. For example,

struct A<T> {
protocol P {
func requirement() -> T
}
}

Presumably A<Int>.P and A<String>.P are distinct types, and A.P has a hidden associated type corresponding to the type parameter ’T’?

The other case is problematic too — the nested type might refer to an associated type of the outer protocol:

protocol P {
associatedtype A

struct T {
var value: A
}
}

Now writing P.T does not make sense, for the same reason that we cannot form an existential of type P.A. We could prohibit references to outer associated types of this form, or we could figure out some way to give it a meaning. If C is a concrete type conforming to P, then certainly C.T makes sense, for instance. Internally, the nested type A.T could have a hidden ‘Self’ generic type parameter, so that writing C.T is really the same as P.T<C>.

Protocols nested inside protocols also have the same issue.

FWIW, in almost all the situations where I’ve wanted to nest types inside protocols and generic types, it’s only as a namespacing convenience. Most often, it’s an enum type that’s used only by a single method, and having it at the top of the module namespace adds clutter.

Here’s a real life example pared down. I wish I could do this:

public struct ResponseContentTransformer<InputContentType, OutputContentType>: ResponseTransformer {

  public init(onInputTypeMismatch mismatchAction: InputTypeMismatchAction = .error) {
    ...
  }

  public enum InputTypeMismatchAction { // Does not depend on generic types above
    case error
    case skip
    case skipIfOutputTypeMatches
  }

}

InputTypeMismatchAction is tightly associated with ResponseContentTransformer, and is confusing as a top-level type.

What do you think about providing a “no captures” modifier for nested types — like static inner classes in Java? Then Swift could provide the namespace nesting I wish for this without having to resolve the trickier type capture questions yet.

Alternatively, what if (1) outer types aren’t capture unless they’re referenced, and (2) nesting is only illegal if there’s a capture? Then my code above would compile, as would this:

public struct S<T> {
  public enum Foo {
    case yin
    case yang
  }
}

…but this wouldn’t:

public struct S<T> {
  public enum Foo {
    case yin(thing: T) // capture of T illegal (for now)
    case yang
  }
}

Either of these approaches would allow hygienic namespacing now while leaving the door open to outer type capture in the future.

Yeah, this makes sense for a first cut at this feature.

Slava

Should I take a crack at writing up a proposal for this? Now? After ABI work is done? (Probably the latter “OK if no captures” approach?) Eager to help; don’t want to be in the way.

Just speaking for myself and not the whole team — I think you can submit the proposal at any time, we’re unlikely to get around to doing it, if you want to take a crack that would be great (again, with ‘no captures’ it’s “trivial”).

Slava

P

Sorry, let this slip. Proposal sent - https://github.com/apple/swift-evolution/pull/552

2 Likes

+1

I really want protocols which can be namespaced inside structs/classes!

Thanks,
Jon

···

On Jan 17, 2017, at 4:07 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

On Nov 5, 2016, at 2:44 AM, Karl via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On 2 Nov 2016, at 20:54, Slava Pestov <spestov@apple.com <mailto:spestov@apple.com>> wrote:

On Nov 2, 2016, at 8:32 AM, Paul Cantrell <cantrell@pobox.com <mailto:cantrell@pobox.com>> wrote:

On Oct 24, 2016, at 4:43 PM, Slava Pestov <spestov@apple.com <mailto:spestov@apple.com>> wrote:

On Oct 24, 2016, at 8:12 AM, Paul Cantrell <cantrell@pobox.com <mailto:cantrell@pobox.com>> wrote:

On Oct 24, 2016, at 5:09 AM, Slava Pestov via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

However protocols nested inside types and types nested inside protocols is still not supported, because protocols introduce a separate series of issues involving associated types and the ’Self’ type.

The hard part of getting nested generics right is what to do if a nested type ‘captures’ generic parameters of the outer type. For non-protocol types, the behavior here is pretty straightforward.

If we allow protocols to be nested inside other types, we have to decide what to do if the protocol ‘closes over’ generic parameters of the outer type. For example,

struct A<T> {
protocol P {
func requirement() -> T
}
}

Presumably A<Int>.P and A<String>.P are distinct types, and A.P has a hidden associated type corresponding to the type parameter ’T’?

The other case is problematic too — the nested type might refer to an associated type of the outer protocol:

protocol P {
associatedtype A

struct T {
var value: A
}
}

Now writing P.T does not make sense, for the same reason that we cannot form an existential of type P.A. We could prohibit references to outer associated types of this form, or we could figure out some way to give it a meaning. If C is a concrete type conforming to P, then certainly C.T makes sense, for instance. Internally, the nested type A.T could have a hidden ‘Self’ generic type parameter, so that writing C.T is really the same as P.T<C>.

Protocols nested inside protocols also have the same issue.

FWIW, in almost all the situations where I’ve wanted to nest types inside protocols and generic types, it’s only as a namespacing convenience. Most often, it’s an enum type that’s used only by a single method, and having it at the top of the module namespace adds clutter.

Here’s a real life example pared down. I wish I could do this:

public struct ResponseContentTransformer<InputContentType, OutputContentType>: ResponseTransformer {

  public init(onInputTypeMismatch mismatchAction: InputTypeMismatchAction = .error) {
    ...
  }

  public enum InputTypeMismatchAction { // Does not depend on generic types above
    case error
    case skip
    case skipIfOutputTypeMatches
  }

}

InputTypeMismatchAction is tightly associated with ResponseContentTransformer, and is confusing as a top-level type.

What do you think about providing a “no captures” modifier for nested types — like static inner classes in Java? Then Swift could provide the namespace nesting I wish for this without having to resolve the trickier type capture questions yet.

Alternatively, what if (1) outer types aren’t capture unless they’re referenced, and (2) nesting is only illegal if there’s a capture? Then my code above would compile, as would this:

public struct S<T> {
  public enum Foo {
    case yin
    case yang
  }
}

…but this wouldn’t:

public struct S<T> {
  public enum Foo {
    case yin(thing: T) // capture of T illegal (for now)
    case yang
  }
}

Either of these approaches would allow hygienic namespacing now while leaving the door open to outer type capture in the future.

Yeah, this makes sense for a first cut at this feature.

Slava

Should I take a crack at writing up a proposal for this? Now? After ABI work is done? (Probably the latter “OK if no captures” approach?) Eager to help; don’t want to be in the way.

Just speaking for myself and not the whole team — I think you can submit the proposal at any time, we’re unlikely to get around to doing it, if you want to take a crack that would be great (again, with ‘no captures’ it’s “trivial”).

Slava

P

Sorry, let this slip. Proposal sent - https://github.com/apple/swift-evolution/pull/552

(Coming back to this after a very long time)

One very high-level comment: one of your early examples is making the Delegate of a view. Did you consider making this an Objective-C translation rule as well, so that *Delegate and *DataSource protocols would be imported as nested types within a class with the name signified by *, e.g.,

  class UITableView {
    @objc(UITableViewDataSource)
    protocol DataSource { … }

    @objc(UITableViewDelegate)
    protocol Delegate { … }
  }

  - Doug

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

+1 to all of that. The proposal in general and the import rule.

···

On Tue, Jan 17, 2017 at 7:07 PM, Douglas Gregor via swift-evolution < swift-evolution@swift.org> wrote:

On Nov 5, 2016, at 2:44 AM, Karl via swift-evolution < > swift-evolution@swift.org> wrote:

On 2 Nov 2016, at 20:54, Slava Pestov <spestov@apple.com> wrote:

On Nov 2, 2016, at 8:32 AM, Paul Cantrell <cantrell@pobox.com> wrote:

On Oct 24, 2016, at 4:43 PM, Slava Pestov <spestov@apple.com> wrote:

On Oct 24, 2016, at 8:12 AM, Paul Cantrell <cantrell@pobox.com> wrote:

On Oct 24, 2016, at 5:09 AM, Slava Pestov via swift-evolution < > swift-evolution@swift.org> wrote:

However protocols nested inside types and types nested inside protocols is
still not supported, because protocols introduce a separate series of
issues involving associated types and the ’Self’ type.

The hard part of getting nested generics right is what to do if a nested
type ‘captures’ generic parameters of the outer type. For non-protocol
types, the behavior here is pretty straightforward.

If we allow protocols to be nested inside other types, we have to decide
what to do if the protocol ‘closes over’ generic parameters of the outer
type. For example,

struct A<T> {
protocol P {
func requirement() -> T
}
}

Presumably A<Int>.P and A<String>.P are distinct types, and A.P has a
hidden associated type corresponding to the type parameter ’T’?

The other case is problematic too — the nested type might refer to an
associated type of the outer protocol:

protocol P {
associatedtype A

struct T {
var value: A
}
}

Now writing P.T does not make sense, for the same reason that we cannot
form an existential of type P.A. We could prohibit references to outer
associated types of this form, or we could figure out some way to give it a
meaning. If C is a concrete type conforming to P, then certainly C.T makes
sense, for instance. Internally, the nested type A.T could have a hidden
‘Self’ generic type parameter, so that writing C.T is really the same as
P.T<C>.

Protocols nested inside protocols also have the same issue.

FWIW, in almost all the situations where I’ve wanted to nest types inside
protocols and generic types, it’s only as a namespacing convenience. Most
often, it’s an enum type that’s used only by a single method, and having it
at the top of the module namespace adds clutter.

Here’s a real life example pared down. I wish I could do this:

public struct ResponseContentTransformer<InputContentType,
>: ResponseTransformer {

  public init(onInputTypeMismatch mismatchAction: InputTypeMismatchAction
= .error) {
    ...
  }

  public enum InputTypeMismatchAction { // Does not depend on generic
types above
    case error
    case skip
    case skipIfOutputTypeMatches
  }

}

InputTypeMismatchAction is tightly associated with
ResponseContentTransformer, and is confusing as a top-level type.

What do you think about providing a “no captures” modifier for nested
types — like static inner classes in Java? Then Swift could provide the
namespace nesting I wish for this without having to resolve the trickier
type capture questions yet.

Alternatively, what if (1) outer types aren’t capture unless they’re
referenced, and (2) nesting is only illegal if there’s a capture? Then my
code above would compile, as would this:

public struct S<T> {
  public enum Foo {
    case yin
    case yang
  }
}

…but this wouldn’t:

public struct S<T> {
  public enum Foo {
    case yin(thing: T) // capture of T illegal (for now)
    case yang
  }
}

Either of these approaches would allow hygienic namespacing now while
leaving the door open to outer type capture in the future.

Yeah, this makes sense for a first cut at this feature.

Slava

Should I take a crack at writing up a proposal for this? Now? After ABI
work is done? (Probably the latter “OK if no captures” approach?) Eager to
help; don’t want to be in the way.

Just speaking for myself and not the whole team — I think you can submit
the proposal at any time, we’re unlikely to get around to doing it, if you
want to take a crack that would be great (again, with ‘no captures’ it’s
“trivial”).

Slava

P

Sorry, let this slip. Proposal sent - Apple · GitHub
swift-evolution/pull/552

(Coming back to this after a very long time)

One very high-level comment: one of your early examples is making the
Delegate of a view. Did you consider making this an Objective-C translation
rule as well, so that *Delegate and *DataSource protocols would be imported
as nested types within a class with the name signified by *, e.g.,

class UITableView {
  @objc(UITableViewDataSource)
  protocol DataSource { … }

  @objc(UITableViewDelegate)
  protocol Delegate { … }
}

- Doug

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

Yes, and platform SDK changes are mentioned under “Source Compatibility”.

I’ve removed the standard library stuff from it now, that can happen later. Would we be able to get the ball rolling on getting it reviewed?

Thanks

- Karl

···

On 18 Jan 2017, at 01:07, Douglas Gregor <dgregor@apple.com> wrote:

On Nov 5, 2016, at 2:44 AM, Karl via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On 2 Nov 2016, at 20:54, Slava Pestov <spestov@apple.com <mailto:spestov@apple.com>> wrote:

On Nov 2, 2016, at 8:32 AM, Paul Cantrell <cantrell@pobox.com <mailto:cantrell@pobox.com>> wrote:

On Oct 24, 2016, at 4:43 PM, Slava Pestov <spestov@apple.com <mailto:spestov@apple.com>> wrote:

On Oct 24, 2016, at 8:12 AM, Paul Cantrell <cantrell@pobox.com <mailto:cantrell@pobox.com>> wrote:

On Oct 24, 2016, at 5:09 AM, Slava Pestov via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

However protocols nested inside types and types nested inside protocols is still not supported, because protocols introduce a separate series of issues involving associated types and the ’Self’ type.

The hard part of getting nested generics right is what to do if a nested type ‘captures’ generic parameters of the outer type. For non-protocol types, the behavior here is pretty straightforward.

If we allow protocols to be nested inside other types, we have to decide what to do if the protocol ‘closes over’ generic parameters of the outer type. For example,

struct A<T> {
protocol P {
func requirement() -> T
}
}

Presumably A<Int>.P and A<String>.P are distinct types, and A.P has a hidden associated type corresponding to the type parameter ’T’?

The other case is problematic too — the nested type might refer to an associated type of the outer protocol:

protocol P {
associatedtype A

struct T {
var value: A
}
}

Now writing P.T does not make sense, for the same reason that we cannot form an existential of type P.A. We could prohibit references to outer associated types of this form, or we could figure out some way to give it a meaning. If C is a concrete type conforming to P, then certainly C.T makes sense, for instance. Internally, the nested type A.T could have a hidden ‘Self’ generic type parameter, so that writing C.T is really the same as P.T<C>.

Protocols nested inside protocols also have the same issue.

FWIW, in almost all the situations where I’ve wanted to nest types inside protocols and generic types, it’s only as a namespacing convenience. Most often, it’s an enum type that’s used only by a single method, and having it at the top of the module namespace adds clutter.

Here’s a real life example pared down. I wish I could do this:

public struct ResponseContentTransformer<InputContentType, OutputContentType>: ResponseTransformer {

  public init(onInputTypeMismatch mismatchAction: InputTypeMismatchAction = .error) {
    ...
  }

  public enum InputTypeMismatchAction { // Does not depend on generic types above
    case error
    case skip
    case skipIfOutputTypeMatches
  }

}

InputTypeMismatchAction is tightly associated with ResponseContentTransformer, and is confusing as a top-level type.

What do you think about providing a “no captures” modifier for nested types — like static inner classes in Java? Then Swift could provide the namespace nesting I wish for this without having to resolve the trickier type capture questions yet.

Alternatively, what if (1) outer types aren’t capture unless they’re referenced, and (2) nesting is only illegal if there’s a capture? Then my code above would compile, as would this:

public struct S<T> {
  public enum Foo {
    case yin
    case yang
  }
}

…but this wouldn’t:

public struct S<T> {
  public enum Foo {
    case yin(thing: T) // capture of T illegal (for now)
    case yang
  }
}

Either of these approaches would allow hygienic namespacing now while leaving the door open to outer type capture in the future.

Yeah, this makes sense for a first cut at this feature.

Slava

Should I take a crack at writing up a proposal for this? Now? After ABI work is done? (Probably the latter “OK if no captures” approach?) Eager to help; don’t want to be in the way.

Just speaking for myself and not the whole team — I think you can submit the proposal at any time, we’re unlikely to get around to doing it, if you want to take a crack that would be great (again, with ‘no captures’ it’s “trivial”).

Slava

P

Sorry, let this slip. Proposal sent - https://github.com/apple/swift-evolution/pull/552

(Coming back to this after a very long time)

One very high-level comment: one of your early examples is making the Delegate of a view. Did you consider making this an Objective-C translation rule as well, so that *Delegate and *DataSource protocols would be imported as nested types within a class with the name signified by *, e.g.,

  class UITableView {
    @objc(UITableViewDataSource)
    protocol DataSource { … }

    @objc(UITableViewDelegate)
    protocol Delegate { … }
  }

  - Doug

+1. Nesting protocols inside an “owning" type and importing prefixed Objective-C protocols this way would be great!

···

On Jan 17, 2017, at 6:15 PM, T.J. Usiyan via swift-evolution <swift-evolution@swift.org> wrote:

+1 to all of that. The proposal in general and the import rule.

On Tue, Jan 17, 2017 at 7:07 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Nov 5, 2016, at 2:44 AM, Karl via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On 2 Nov 2016, at 20:54, Slava Pestov <spestov@apple.com <mailto:spestov@apple.com>> wrote:

On Nov 2, 2016, at 8:32 AM, Paul Cantrell <cantrell@pobox.com <mailto:cantrell@pobox.com>> wrote:

On Oct 24, 2016, at 4:43 PM, Slava Pestov <spestov@apple.com <mailto:spestov@apple.com>> wrote:

On Oct 24, 2016, at 8:12 AM, Paul Cantrell <cantrell@pobox.com <mailto:cantrell@pobox.com>> wrote:

On Oct 24, 2016, at 5:09 AM, Slava Pestov via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

However protocols nested inside types and types nested inside protocols is still not supported, because protocols introduce a separate series of issues involving associated types and the ’Self’ type.

The hard part of getting nested generics right is what to do if a nested type ‘captures’ generic parameters of the outer type. For non-protocol types, the behavior here is pretty straightforward.

If we allow protocols to be nested inside other types, we have to decide what to do if the protocol ‘closes over’ generic parameters of the outer type. For example,

struct A<T> {
protocol P {
func requirement() -> T
}
}

Presumably A<Int>.P and A<String>.P are distinct types, and A.P has a hidden associated type corresponding to the type parameter ’T’?

The other case is problematic too — the nested type might refer to an associated type of the outer protocol:

protocol P {
associatedtype A

struct T {
var value: A
}
}

Now writing P.T does not make sense, for the same reason that we cannot form an existential of type P.A. We could prohibit references to outer associated types of this form, or we could figure out some way to give it a meaning. If C is a concrete type conforming to P, then certainly C.T makes sense, for instance. Internally, the nested type A.T could have a hidden ‘Self’ generic type parameter, so that writing C.T is really the same as P.T<C>.

Protocols nested inside protocols also have the same issue.

FWIW, in almost all the situations where I’ve wanted to nest types inside protocols and generic types, it’s only as a namespacing convenience. Most often, it’s an enum type that’s used only by a single method, and having it at the top of the module namespace adds clutter.

Here’s a real life example pared down. I wish I could do this:

public struct ResponseContentTransformer<InputContentType, OutputContentType>: ResponseTransformer {

  public init(onInputTypeMismatch mismatchAction: InputTypeMismatchAction = .error) {
    ...
  }

  public enum InputTypeMismatchAction { // Does not depend on generic types above
    case error
    case skip
    case skipIfOutputTypeMatches
  }

}

InputTypeMismatchAction is tightly associated with ResponseContentTransformer, and is confusing as a top-level type.

What do you think about providing a “no captures” modifier for nested types — like static inner classes in Java? Then Swift could provide the namespace nesting I wish for this without having to resolve the trickier type capture questions yet.

Alternatively, what if (1) outer types aren’t capture unless they’re referenced, and (2) nesting is only illegal if there’s a capture? Then my code above would compile, as would this:

public struct S<T> {
  public enum Foo {
    case yin
    case yang
  }
}

…but this wouldn’t:

public struct S<T> {
  public enum Foo {
    case yin(thing: T) // capture of T illegal (for now)
    case yang
  }
}

Either of these approaches would allow hygienic namespacing now while leaving the door open to outer type capture in the future.

Yeah, this makes sense for a first cut at this feature.

Slava

Should I take a crack at writing up a proposal for this? Now? After ABI work is done? (Probably the latter “OK if no captures” approach?) Eager to help; don’t want to be in the way.

Just speaking for myself and not the whole team — I think you can submit the proposal at any time, we’re unlikely to get around to doing it, if you want to take a crack that would be great (again, with ‘no captures’ it’s “trivial”).

Slava

P

Sorry, let this slip. Proposal sent - https://github.com/apple/swift-evolution/pull/552

(Coming back to this after a very long time)

One very high-level comment: one of your early examples is making the Delegate of a view. Did you consider making this an Objective-C translation rule as well, so that *Delegate and *DataSource protocols would be imported as nested types within a class with the name signified by *, e.g.,

  class UITableView {
    @objc(UITableViewDataSource)
    protocol DataSource { … }

    @objc(UITableViewDelegate)
    protocol Delegate { … }
  }

  - Doug

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

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

1 Like

I left some review comments here:

Slava

···

On Jan 18, 2017, at 12:17 AM, Karl Wagner <razielim@gmail.com> wrote:

On 18 Jan 2017, at 01:07, Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> wrote:

On Nov 5, 2016, at 2:44 AM, Karl via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On 2 Nov 2016, at 20:54, Slava Pestov <spestov@apple.com <mailto:spestov@apple.com>> wrote:

On Nov 2, 2016, at 8:32 AM, Paul Cantrell <cantrell@pobox.com <mailto:cantrell@pobox.com>> wrote:

On Oct 24, 2016, at 4:43 PM, Slava Pestov <spestov@apple.com <mailto:spestov@apple.com>> wrote:

On Oct 24, 2016, at 8:12 AM, Paul Cantrell <cantrell@pobox.com <mailto:cantrell@pobox.com>> wrote:

On Oct 24, 2016, at 5:09 AM, Slava Pestov via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

However protocols nested inside types and types nested inside protocols is still not supported, because protocols introduce a separate series of issues involving associated types and the ’Self’ type.

The hard part of getting nested generics right is what to do if a nested type ‘captures’ generic parameters of the outer type. For non-protocol types, the behavior here is pretty straightforward.

If we allow protocols to be nested inside other types, we have to decide what to do if the protocol ‘closes over’ generic parameters of the outer type. For example,

struct A<T> {
protocol P {
func requirement() -> T
}
}

Presumably A<Int>.P and A<String>.P are distinct types, and A.P has a hidden associated type corresponding to the type parameter ’T’?

The other case is problematic too — the nested type might refer to an associated type of the outer protocol:

protocol P {
associatedtype A

struct T {
var value: A
}
}

Now writing P.T does not make sense, for the same reason that we cannot form an existential of type P.A. We could prohibit references to outer associated types of this form, or we could figure out some way to give it a meaning. If C is a concrete type conforming to P, then certainly C.T makes sense, for instance. Internally, the nested type A.T could have a hidden ‘Self’ generic type parameter, so that writing C.T is really the same as P.T<C>.

Protocols nested inside protocols also have the same issue.

FWIW, in almost all the situations where I’ve wanted to nest types inside protocols and generic types, it’s only as a namespacing convenience. Most often, it’s an enum type that’s used only by a single method, and having it at the top of the module namespace adds clutter.

Here’s a real life example pared down. I wish I could do this:

public struct ResponseContentTransformer<InputContentType, OutputContentType>: ResponseTransformer {

  public init(onInputTypeMismatch mismatchAction: InputTypeMismatchAction = .error) {
    ...
  }

  public enum InputTypeMismatchAction { // Does not depend on generic types above
    case error
    case skip
    case skipIfOutputTypeMatches
  }

}

InputTypeMismatchAction is tightly associated with ResponseContentTransformer, and is confusing as a top-level type.

What do you think about providing a “no captures” modifier for nested types — like static inner classes in Java? Then Swift could provide the namespace nesting I wish for this without having to resolve the trickier type capture questions yet.

Alternatively, what if (1) outer types aren’t capture unless they’re referenced, and (2) nesting is only illegal if there’s a capture? Then my code above would compile, as would this:

public struct S<T> {
  public enum Foo {
    case yin
    case yang
  }
}

…but this wouldn’t:

public struct S<T> {
  public enum Foo {
    case yin(thing: T) // capture of T illegal (for now)
    case yang
  }
}

Either of these approaches would allow hygienic namespacing now while leaving the door open to outer type capture in the future.

Yeah, this makes sense for a first cut at this feature.

Slava

Should I take a crack at writing up a proposal for this? Now? After ABI work is done? (Probably the latter “OK if no captures” approach?) Eager to help; don’t want to be in the way.

Just speaking for myself and not the whole team — I think you can submit the proposal at any time, we’re unlikely to get around to doing it, if you want to take a crack that would be great (again, with ‘no captures’ it’s “trivial”).

Slava

P

Sorry, let this slip. Proposal sent - https://github.com/apple/swift-evolution/pull/552

(Coming back to this after a very long time)

One very high-level comment: one of your early examples is making the Delegate of a view. Did you consider making this an Objective-C translation rule as well, so that *Delegate and *DataSource protocols would be imported as nested types within a class with the name signified by *, e.g.,

  class UITableView {
    @objc(UITableViewDataSource)
    protocol DataSource { … }

    @objc(UITableViewDelegate)
    protocol Delegate { … }
  }

  - Doug

Yes, and platform SDK changes are mentioned under “Source Compatibility”.

I’ve removed the standard library stuff from it now, that can happen later. Would we be able to get the ball rolling on getting it reviewed?

Thanks

- Karl

What happened to this? Should we revive this talk? I’d love to finally be able to nest protocols in Swift 5 and clean up my code.

···

Am 18. Januar 2017 um 09:48:20, Slava Pestov via swift-evolution (swift-evolution@swift.org) schrieb:

I left some review comments here:

https://github.com/apple/swift-evolution/commit/ff654e4

Slava

On Jan 18, 2017, at 12:17 AM, Karl Wagner <razielim@gmail.com> wrote:

On 18 Jan 2017, at 01:07, Douglas Gregor <dgregor@apple.com> wrote:

On Nov 5, 2016, at 2:44 AM, Karl via swift-evolution <swift-evolution@swift.org> wrote:

On 2 Nov 2016, at 20:54, Slava Pestov <spestov@apple.com> wrote:

On Nov 2, 2016, at 8:32 AM, Paul Cantrell <cantrell@pobox.com> wrote:

On Oct 24, 2016, at 4:43 PM, Slava Pestov <spestov@apple.com> wrote:

On Oct 24, 2016, at 8:12 AM, Paul Cantrell <cantrell@pobox.com> wrote:

On Oct 24, 2016, at 5:09 AM, Slava Pestov via swift-evolution <swift-evolution@swift.org> wrote:

However protocols nested inside types and types nested inside protocols is still not supported, because protocols introduce a separate series of issues involving associated types and the ’Self’ type.

The hard part of getting nested generics right is what to do if a nested type ‘captures’ generic parameters of the outer type. For non-protocol types, the behavior here is pretty straightforward.

If we allow protocols to be nested inside other types, we have to decide what to do if the protocol ‘closes over’ generic parameters of the outer type. For example,

struct A<T> {
protocol P {
func requirement() -> T
}
}

Presumably A<Int>.P and A<String>.P are distinct types, and A.P has a hidden associated type corresponding to the type parameter ’T’?

The other case is problematic too — the nested type might refer to an associated type of the outer protocol:

protocol P {
associatedtype A

struct T {
var value: A
}
}

Now writing P.T does not make sense, for the same reason that we cannot form an existential of type P.A. We could prohibit references to outer associated types of this form, or we could figure out some way to give it a meaning. If C is a concrete type conforming to P, then certainly C.T makes sense, for instance. Internally, the nested type A.T could have a hidden ‘Self’ generic type parameter, so that writing C.T is really the same as P.T<C>.

Protocols nested inside protocols also have the same issue.

FWIW, in almost all the situations where I’ve wanted to nest types inside protocols and generic types, it’s only as a namespacing convenience. Most often, it’s an enum type that’s used only by a single method, and having it at the top of the module namespace adds clutter.

Here’s a real life example pared down. I wish I could do this:

public struct ResponseContentTransformer<InputContentType, OutputContentType>: ResponseTransformer {

public init(onInputTypeMismatch mismatchAction: InputTypeMismatchAction = .error) {
...
}

public enum InputTypeMismatchAction { // Does not depend on generic types above
case error
case skip
case skipIfOutputTypeMatches
}

}

InputTypeMismatchAction is tightly associated with ResponseContentTransformer, and is confusing as a top-level type.

What do you think about providing a “no captures” modifier for nested types — like static inner classes in Java? Then Swift could provide the namespace nesting I wish for this without having to resolve the trickier type capture questions yet.

Alternatively, what if (1) outer types aren’t capture unless they’re referenced, and (2) nesting is only illegal if there’s a capture? Then my code above would compile, as would this:

public struct S<T> {
public enum Foo {
case yin
case yang
}
}

…but this wouldn’t:

public struct S<T> {
public enum Foo {
case yin(thing: T) // capture of T illegal (for now)
case yang
}
}

Either of these approaches would allow hygienic namespacing now while leaving the door open to outer type capture in the future.

Yeah, this makes sense for a first cut at this feature.

Slava

Should I take a crack at writing up a proposal for this? Now? After ABI work is done? (Probably the latter “OK if no captures” approach?) Eager to help; don’t want to be in the way.

Just speaking for myself and not the whole team — I think you can submit the proposal at any time, we’re unlikely to get around to doing it, if you want to take a crack that would be great (again, with ‘no captures’ it’s “trivial”).

Slava

P

Sorry, let this slip. Proposal sent - Ease restrictions on protocol nesting by karwa · Pull Request #552 · apple/swift-evolution · GitHub

(Coming back to this after a very long time)

One very high-level comment: one of your early examples is making the Delegate of a view. Did you consider making this an Objective-C translation rule as well, so that *Delegate and *DataSource protocols would be imported as nested types within a class with the name signified by *, e.g.,

class UITableView {
@objc(UITableViewDataSource)
protocol DataSource { … }

@objc(UITableViewDelegate)
protocol Delegate { … }
}

- Doug

Yes, and platform SDK changes are mentioned under “Source Compatibility”.

I’ve removed the standard library stuff from it now, that can happen later. Would we be able to get the ball rolling on getting it reviewed?

Thanks

- Karl

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

I would use this pretty much constantly! I thought it was going to be in Swift 4, and have really been missing it…

···

On Nov 26, 2017, at 8:51 AM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

What happened to this? Should we revive this talk? I’d love to finally be able to nest protocols in Swift 5 and clean up my code.

Am 18. Januar 2017 um 09:48:20, Slava Pestov via swift-evolution (swift-evolution@swift.org <mailto:swift-evolution@swift.org>) schrieb:

I left some review comments here:

Update xxxx-ease-protocol-nesting.md · apple/swift-evolution@ff654e4 · GitHub

Slava

On Jan 18, 2017, at 12:17 AM, Karl Wagner <razielim@gmail.com <mailto:razielim@gmail.com>> wrote:

On 18 Jan 2017, at 01:07, Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> wrote:

On Nov 5, 2016, at 2:44 AM, Karl via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On 2 Nov 2016, at 20:54, Slava Pestov <spestov@apple.com <mailto:spestov@apple.com>> wrote:

On Nov 2, 2016, at 8:32 AM, Paul Cantrell <cantrell@pobox.com <mailto:cantrell@pobox.com>> wrote:

On Oct 24, 2016, at 4:43 PM, Slava Pestov <spestov@apple.com <mailto:spestov@apple.com>> wrote:

On Oct 24, 2016, at 8:12 AM, Paul Cantrell <cantrell@pobox.com <mailto:cantrell@pobox.com>> wrote:

On Oct 24, 2016, at 5:09 AM, Slava Pestov via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

However protocols nested inside types and types nested inside protocols is still not supported, because protocols introduce a separate series of issues involving associated types and the ’Self’ type.

The hard part of getting nested generics right is what to do if a nested type ‘captures’ generic parameters of the outer type. For non-protocol types, the behavior here is pretty straightforward.

If we allow protocols to be nested inside other types, we have to decide what to do if the protocol ‘closes over’ generic parameters of the outer type. For example,

struct A<T> {
protocol P {
func requirement() -> T
}
}

Presumably A<Int>.P and A<String>.P are distinct types, and A.P has a hidden associated type corresponding to the type parameter ’T’?

The other case is problematic too — the nested type might refer to an associated type of the outer protocol:

protocol P {
associatedtype A

struct T {
var value: A
}
}

Now writing P.T does not make sense, for the same reason that we cannot form an existential of type P.A. We could prohibit references to outer associated types of this form, or we could figure out some way to give it a meaning. If C is a concrete type conforming to P, then certainly C.T makes sense, for instance. Internally, the nested type A.T could have a hidden ‘Self’ generic type parameter, so that writing C.T is really the same as P.T<C>.

Protocols nested inside protocols also have the same issue.

FWIW, in almost all the situations where I’ve wanted to nest types inside protocols and generic types, it’s only as a namespacing convenience. Most often, it’s an enum type that’s used only by a single method, and having it at the top of the module namespace adds clutter.

Here’s a real life example pared down. I wish I could do this:

public struct ResponseContentTransformer<InputContentType, OutputContentType>: ResponseTransformer {

  public init(onInputTypeMismatch mismatchAction: InputTypeMismatchAction = .error) {
    ...
  }

  public enum InputTypeMismatchAction { // Does not depend on generic types above
    case error
    case skip
    case skipIfOutputTypeMatches
  }

}

InputTypeMismatchAction is tightly associated with ResponseContentTransformer, and is confusing as a top-level type.

What do you think about providing a “no captures” modifier for nested types — like static inner classes in Java? Then Swift could provide the namespace nesting I wish for this without having to resolve the trickier type capture questions yet.

Alternatively, what if (1) outer types aren’t capture unless they’re referenced, and (2) nesting is only illegal if there’s a capture? Then my code above would compile, as would this:

public struct S<T> {
  public enum Foo {
    case yin
    case yang
  }
}

…but this wouldn’t:

public struct S<T> {
  public enum Foo {
    case yin(thing: T) // capture of T illegal (for now)
    case yang
  }
}

Either of these approaches would allow hygienic namespacing now while leaving the door open to outer type capture in the future.

Yeah, this makes sense for a first cut at this feature.

Slava

Should I take a crack at writing up a proposal for this? Now? After ABI work is done? (Probably the latter “OK if no captures” approach?) Eager to help; don’t want to be in the way.

Just speaking for myself and not the whole team — I think you can submit the proposal at any time, we’re unlikely to get around to doing it, if you want to take a crack that would be great (again, with ‘no captures’ it’s “trivial”).

Slava

P

Sorry, let this slip. Proposal sent - Ease restrictions on protocol nesting by karwa · Pull Request #552 · apple/swift-evolution · GitHub

(Coming back to this after a very long time)

One very high-level comment: one of your early examples is making the Delegate of a view. Did you consider making this an Objective-C translation rule as well, so that *Delegate and *DataSource protocols would be imported as nested types within a class with the name signified by *, e.g.,

class UITableView {
  @objc(UITableViewDataSource)
  protocol DataSource { … }

  @objc(UITableViewDelegate)
  protocol Delegate { … }
}

- Doug

Yes, and platform SDK changes are mentioned under “Source Compatibility”.

I’ve removed the standard library stuff from it now, that can happen later. Would we be able to get the ball rolling on getting it reviewed?

Thanks

- Karl

_______________________________________________
swift-evolution mailing list
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

Is there another proposal directly related to namespacing ? This would seem like addressing a bit of the same issue..
As an example i would much prefer writing something like :

Package UITableView
Class Table : UIView
Class Cell : UIView
Protocol Delegate
Protocol Datasource

With each class in a different file, under a common folder named after the namespace, rather than a mumbo jumbo of structs enums and protocols all wrapping each others.

The only advantage of nesting i would think about is to be able to capture something from the parent context. But since that’s a potential snake pit, i’d rather not even start going in that direction.

···

Le 27 nov. 2017 à 00:12, Jonathan Hull via swift-evolution <swift-evolution@swift.org> a écrit :

I would use this pretty much constantly! I thought it was going to be in Swift 4, and have really been missing it…

On Nov 26, 2017, at 8:51 AM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

What happened to this? Should we revive this talk? I’d love to finally be able to nest protocols in Swift 5 and clean up my code.

Am 18. Januar 2017 um 09:48:20, Slava Pestov via swift-evolution (swift-evolution@swift.org) schrieb:

I left some review comments here:

Update xxxx-ease-protocol-nesting.md · apple/swift-evolution@ff654e4 · GitHub

Slava

On Jan 18, 2017, at 12:17 AM, Karl Wagner <razielim@gmail.com> wrote:

On 18 Jan 2017, at 01:07, Douglas Gregor <dgregor@apple.com> wrote:

On Nov 5, 2016, at 2:44 AM, Karl via swift-evolution <swift-evolution@swift.org> wrote:

On 2 Nov 2016, at 20:54, Slava Pestov <spestov@apple.com> wrote:

On Nov 2, 2016, at 8:32 AM, Paul Cantrell <cantrell@pobox.com> wrote:

On Oct 24, 2016, at 4:43 PM, Slava Pestov <spestov@apple.com> wrote:

On Oct 24, 2016, at 8:12 AM, Paul Cantrell <cantrell@pobox.com> wrote:

On Oct 24, 2016, at 5:09 AM, Slava Pestov via swift-evolution <swift-evolution@swift.org> wrote:

However protocols nested inside types and types nested inside protocols is still not supported, because protocols introduce a separate series of issues involving associated types and the ’Self’ type.

The hard part of getting nested generics right is what to do if a nested type ‘captures’ generic parameters of the outer type. For non-protocol types, the behavior here is pretty straightforward.

If we allow protocols to be nested inside other types, we have to decide what to do if the protocol ‘closes over’ generic parameters of the outer type. For example,

struct A<T> {
protocol P {
func requirement() -> T
}
}

Presumably A<Int>.P and A<String>.P are distinct types, and A.P has a hidden associated type corresponding to the type parameter ’T’?

The other case is problematic too — the nested type might refer to an associated type of the outer protocol:

protocol P {
associatedtype A

struct T {
var value: A
}
}

Now writing P.T does not make sense, for the same reason that we cannot form an existential of type P.A. We could prohibit references to outer associated types of this form, or we could figure out some way to give it a meaning. If C is a concrete type conforming to P, then certainly C.T makes sense, for instance. Internally, the nested type A.T could have a hidden ‘Self’ generic type parameter, so that writing C.T is really the same as P.T<C>.

Protocols nested inside protocols also have the same issue.

FWIW, in almost all the situations where I’ve wanted to nest types inside protocols and generic types, it’s only as a namespacing convenience. Most often, it’s an enum type that’s used only by a single method, and having it at the top of the module namespace adds clutter.

Here’s a real life example pared down. I wish I could do this:

public struct ResponseContentTransformer<InputContentType, OutputContentType>: ResponseTransformer {

  public init(onInputTypeMismatch mismatchAction: InputTypeMismatchAction = .error) {
    ...
  }

  public enum InputTypeMismatchAction { // Does not depend on generic types above
    case error
    case skip
    case skipIfOutputTypeMatches
  }

}

InputTypeMismatchAction is tightly associated with ResponseContentTransformer, and is confusing as a top-level type.

What do you think about providing a “no captures” modifier for nested types — like static inner classes in Java? Then Swift could provide the namespace nesting I wish for this without having to resolve the trickier type capture questions yet.

Alternatively, what if (1) outer types aren’t capture unless they’re referenced, and (2) nesting is only illegal if there’s a capture? Then my code above would compile, as would this:

public struct S<T> {
  public enum Foo {
    case yin
    case yang
  }
}

…but this wouldn’t:

public struct S<T> {
  public enum Foo {
    case yin(thing: T) // capture of T illegal (for now)
    case yang
  }
}

Either of these approaches would allow hygienic namespacing now while leaving the door open to outer type capture in the future.

Yeah, this makes sense for a first cut at this feature.

Slava

Should I take a crack at writing up a proposal for this? Now? After ABI work is done? (Probably the latter “OK if no captures” approach?) Eager to help; don’t want to be in the way.

Just speaking for myself and not the whole team — I think you can submit the proposal at any time, we’re unlikely to get around to doing it, if you want to take a crack that would be great (again, with ‘no captures’ it’s “trivial”).

Slava

P

Sorry, let this slip. Proposal sent - https://github.com/apple/swift-evolution/pull/552

(Coming back to this after a very long time)

One very high-level comment: one of your early examples is making the Delegate of a view. Did you consider making this an Objective-C translation rule as well, so that *Delegate and *DataSource protocols would be imported as nested types within a class with the name signified by *, e.g.,

class UITableView {
  @objc(UITableViewDataSource)
  protocol DataSource { … }

  @objc(UITableViewDelegate)
  protocol Delegate { … }
}

- Doug

Yes, and platform SDK changes are mentioned under “Source Compatibility”.

I’ve removed the standard library stuff from it now, that can happen later. Would we be able to get the ball rolling on getting it reviewed?

Thanks

- Karl

_______________________________________________
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

Still looking for this feature! 3 years later, but I believe this would be really beneficial

11 Likes

I, myself, would love to see this feature.

3 Likes

I wonder if it's possible for some version of this to get into Swift 6 (or even 5.x if it's non-breaking)? I would love this even if it were restricted to protocols being nested in non-generic types. Currently, every time I write a new feature, it's easy to nest some types in each other, but any protocol has to awkwardly live on the outside of any other type. It's a small thing, but I think it's important.

11 Likes

I think nesting protocols inside non-generic types should be pretty uncontroversial. I can give you some guidance if you want to write up a pitch and implement it.

8 Likes

I would be up for that, though I must admit it will take quite a bit of learning on my part and perhaps more than a little bit of guidance as I'd be entirely new to the compiler world. :sweat_smile:

2 Likes

I would love this, +1

1 Like

I, too, am a strong +1 on this. The last official word I can find on this is an old deferral (2017).

In my case, I really want to nest an enum in a Protocol, like many of the others in this thread.