Allow nested protocol declarations

Is that a bad, or are you just pointing out that this proposal would implicitly add parameterized protocols to Swift? I don’t think they’re *exactly* the same as “naked” parameterized protocols, since you wouldn’t be able access a nested protocol without specifying all the container type’s generic parameters, but maybe that’s a difference that makes no difference.

- Dave Sweeris

···

On Apr 28, 2016, at 6:27 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

Note that this cannot work when any enclosing type is generic, e.g.,

class MyController<T> {
  protocol Delegate {
    // I’ve just created a parameterized protocol!
  }
}

Otherwise, I don’t see any issues with the proposal, and I like that it eliminates a large number of top-level names.

  - Doug

You're right and since you can't nest any kind of type in a generic type
right, that wouldn't work even with this proposal. However, in the generics
manifesto they did say that they intend to remove that restriction. If they
do lift the restriction, they may have to leave an exception in place for
protocols.

···

On Thu, Apr 28, 2016 at 5:27 PM, Douglas Gregor <dgregor@apple.com> wrote:

On Apr 28, 2016, at 10:15 AM, Brad Hilton via swift-evolution < > swift-evolution@swift.org> wrote:

Type nesting allows some convenient and straightforward semantics that we
see inside the Swift standard library such as views on String like
String.CharacterView, String.UnicodeScalarView, etc. However a protocol
cannot be nested in a type and gives a non-obvious error that the
“Declaration is only valid at file scope.” Just as other nested types allow
proper contextual scoping, a nested protocol could make a lot sense for a
number of patterns. For example, there are many “Delegate” protocols
throughout the Cocoa frameworks. Here’s a controller/delegate pattern
before and after type nesting:

// Without type nesting

protocol MyControllerDelegate : class {

}

class MyController {

    weak var delegate: MyControllerDelegate?

}

// With type nesting

class MyController {

    weak var delegate: Delegate?

    protocol Delegate : class {

    }

}

Though the change is mostly semantics, it does allow an explicit
association between My Controller and the Delegate instead of only a named
association. It also cleans up the module name space like other nested
types and makes associated protocols more discoverable in my opinion.

I’d love to hear everyone’s thoughts.

Note that this cannot work when any enclosing type is generic, e.g.,

class MyController<T> {
  protocol Delegate {
    // I’ve just created a parameterized protocol!
  }
}

Otherwise, I don’t see any issues with the proposal, and I like that it
eliminates a large number of top-level names.

- Doug

+1 very handy to associate types together

···

On Friday, 29 April 2016, Michael Peternell via swift-evolution < swift-evolution@swift.org> wrote:

I think that would be a good feature.

> Am 28.04.2016 um 19:15 schrieb Brad Hilton via swift-evolution <
swift-evolution@swift.org <javascript:;>>:
>
> Type nesting allows some convenient and straightforward semantics that
we see inside the Swift standard library such as views on String like
String.CharacterView, String.UnicodeScalarView, etc. However a protocol
cannot be nested in a type and gives a non-obvious error that the
“Declaration is only valid at file scope.” Just as other nested types allow
proper contextual scoping, a nested protocol could make a lot sense for a
number of patterns. For example, there are many “Delegate” protocols
throughout the Cocoa frameworks. Here’s a controller/delegate pattern
before and after type nesting:
>
> // Without type nesting
>
> protocol MyControllerDelegate : class {
>
> }
>
> class MyController {
>
> weak var delegate: MyControllerDelegate?
>
> }
>
> // With type nesting
>
> class MyController {
>
> weak var delegate: Delegate?
>
> protocol Delegate : class {
>
> }
>
> }
>
> Though the change is mostly semantics, it does allow an explicit
association between My Controller and the Delegate instead of only a named
association. It also cleans up the module name space like other nested
types and makes associated protocols more discoverable in my opinion.
>
> I’d love to hear everyone’s thoughts.
>
> Brad Hilton
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <javascript:;>
> https://lists.swift.org/mailman/listinfo/swift-evolution

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

--
-- Howard.

To get over issues like the inner protocol accessing outer quantities like
properties and generics you could add keyword noaccess (bike-shedding
name) to the declaration to make it clear that it does not have access,
e.g.:

class MyController<T> {
  noaccess protocol Delegate {
    // No access to T
  }
}

Java does something along these lines using static and it works out well.
As an aside static is insufficient in Swift because statics have access to
generics in Swift, unlike Java.

···

On Friday, 29 April 2016, Douglas Gregor via swift-evolution < swift-evolution@swift.org> wrote:

On Apr 28, 2016, at 10:15 AM, Brad Hilton via swift-evolution < > swift-evolution@swift.org > <javascript:_e(%7B%7D,'cvml','swift-evolution@swift.org');>> wrote:

Type nesting allows some convenient and straightforward semantics that we
see inside the Swift standard library such as views on String like
String.CharacterView, String.UnicodeScalarView, etc. However a protocol
cannot be nested in a type and gives a non-obvious error that the
“Declaration is only valid at file scope.” Just as other nested types allow
proper contextual scoping, a nested protocol could make a lot sense for a
number of patterns. For example, there are many “Delegate” protocols
throughout the Cocoa frameworks. Here’s a controller/delegate pattern
before and after type nesting:

// Without type nesting

protocol MyControllerDelegate : class {

}

class MyController {

    weak var delegate: MyControllerDelegate?

}

// With type nesting

class MyController {

    weak var delegate: Delegate?

    protocol Delegate : class {

    }

}

Though the change is mostly semantics, it does allow an explicit
association between My Controller and the Delegate instead of only a named
association. It also cleans up the module name space like other nested
types and makes associated protocols more discoverable in my opinion.

I’d love to hear everyone’s thoughts.

Note that this cannot work when any enclosing type is generic, e.g.,

class MyController<T> {
  protocol Delegate {
    // I’ve just created a parameterized protocol!
  }
}

Otherwise, I don’t see any issues with the proposal, and I like that it
eliminates a large number of top-level names.

- Doug

--
-- Howard.

+1 I can’t wait for Swift 3 anymore. This ability would be great.

Additionally what do you think about something like this?

protocol DelegatableType {

 protocol Delegate: class // enforced you to create a nested protocol

 weak var delegate: Delegate? // or Self\.Delegate?

}

class A: DelegatableType {

protocol Delegate \{ /\* create something fancy \*/ \}

var delegate: Delegate? // or A\.Delegate? but I tend to just writing Delegate?

}

···

--
Adrian Zubarev
Sent with Airmail

Am 29. April 2016 bei 09:56:55, Krishna Kumar via swift-evolution (swift-evolution@swift.org) schrieb:

Hey Brad

+1 I think this will make delegate controller pattern a lot more clean and readable.

I was thinking about other areas where this can be useful, and I thought it will be great for property injection from parent controller to children.

class ParentController{
protocol Injection: class{

\}
override func prepareForSegue\(segue: UIStoryboardSegue, sender: AnyObject?\)\{
    let childVC = segue\.destinationViewController as? Injection
    \.\.\.
\}

}

class ChildController: ParentController.Injection{

}

Does this make sense?

-Krishna

On Apr 28, 2016, at 10:45 PM, Brad Hilton via swift-evolution <swift-evolution@swift.org> wrote:

Type nesting allows some convenient and straightforward semantics that we see inside the Swift standard library such as views on String like String.CharacterView, String.UnicodeScalarView, etc. However a protocol cannot be nested in a type and gives a non-obvious error that the “Declaration is only valid at file scope.” Just as other nested types allow proper contextual scoping, a nested protocol could make a lot sense for a number of patterns. For example, there are many “Delegate” protocols throughout the Cocoa frameworks. Here’s a controller/delegate pattern before and after type nesting:

// Without type nesting

protocol MyControllerDelegate : class {

}

class MyController {

weak var delegate: MyControllerDelegate?

}

// With type nesting

class MyController {

weak var delegate: Delegate?

protocol Delegate : class \{
    
\}

}

Though the change is mostly semantics, it does allow an explicit association between My Controller and the Delegate instead of only a named association. It also cleans up the module name space like other nested types and makes associated protocols more discoverable in my opinion.

I’d love to hear everyone’s thoughts.

Brad Hilton
_______________________________________________
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

As a ruby programmer, I have to admit that I've tried to do this in Swift
several times before remembering again that it isn't possible.
+1

···

On Thu, Apr 28, 2016 at 3:44 PM, Howard Lovatt via swift-evolution < swift-evolution@swift.org> wrote:

+1 very handy to associate types together

On Friday, 29 April 2016, Michael Peternell via swift-evolution < > swift-evolution@swift.org> wrote:

I think that would be a good feature.

> Am 28.04.2016 um 19:15 schrieb Brad Hilton via swift-evolution < >> swift-evolution@swift.org>:
>
> Type nesting allows some convenient and straightforward semantics that
we see inside the Swift standard library such as views on String like
String.CharacterView, String.UnicodeScalarView, etc. However a protocol
cannot be nested in a type and gives a non-obvious error that the
“Declaration is only valid at file scope.” Just as other nested types allow
proper contextual scoping, a nested protocol could make a lot sense for a
number of patterns. For example, there are many “Delegate” protocols
throughout the Cocoa frameworks. Here’s a controller/delegate pattern
before and after type nesting:
>
> // Without type nesting
>
> protocol MyControllerDelegate : class {
>
> }
>
> class MyController {
>
> weak var delegate: MyControllerDelegate?
>
> }
>
> // With type nesting
>
> class MyController {
>
> weak var delegate: Delegate?
>
> protocol Delegate : class {
>
> }
>
> }
>
> Though the change is mostly semantics, it does allow an explicit
association between My Controller and the Delegate instead of only a named
association. It also cleans up the module name space like other nested
types and makes associated protocols more discoverable in my opinion.
>
> I’d love to hear everyone’s thoughts.
>
> Brad Hilton
> _______________________________________________
> 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

--
-- Howard.

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

--
kurt@CircleW.org
http://www.CircleW.org/kurt/

It seems like this feature went under the radar for the last releases. Are there any disadvantages about this or was this just deferred until later?

4 Likes

While I know that "+1" posts are discouraged, I believe this deserves one anyway, so here goes:

+1

2 Likes

There was a proposal that was discussed last year. The major sticking point with nesting protocols in extensions regards the capture (or not) of generic parameters from the enclosing type. See the "Detailed Design" section of that proposal for more information.

Doug

unqualified name lookup is also awkward and needs discussion. AFAIK, Swift's current lookup rules aren't clearly documented anywhere as any kind of specification.

protocol Collection {
  struct ABC {}
}

// Note: these are various possible names for the exact same type.

// Should this name exist?
let _: Array<Int>.ABC
// Or should you refer to it like this?
let _: Collection.ABC
// What about this? 
// If not, would it be okay for BDC to define its own type called 'ABC'?
let _: BidirectionalCollection.ABC

Did you meant extension Collection?

// Should this name exist?
let _: Array<Int>.ABC
// Yes it should

// Or should you refer to it like this?
let _: Collection.ABC
// (In current example) Only if `Array<Int>` would have it's own `ABC` that would win, otherwise you can use the above as a shortcut

// What about this? 
// If not, would it be okay for BDC to define its own type called 'ABC'?
let _: BidirectionalCollection.ABC
// DBC can define a custom `ABC` time that would win and it would require you to use `Collection.ABC` if you want to access parents `ABC`

Local type with a colliding name always wins:

struct Foo {}

extension Collection {
  typealias A = Foo
}

struct Test {}

extension Array {
  typealias A = Test
}

let _: Array<Int>.A // Test
let _: BidirectionalCollection.A // Foo

I picked up already a few issues from the previous discussion of your pitch in this thread (had no time to write a formal proposal though, and I have no idea who would be willing to help and implement it):

Yeah I couldn’t find any clear documentation of the existing rules, such as for typealiases. I think I remember that @Slava_Pestov did a lot of work on unqualified lookup; maybe he will know if there’s a spec anywhere.

The Collection.ABC lookup you showed is actually qualified lookup, not unqualified.

And no, there is no spec. :slight_smile:

In fact there are several implementations of name lookup which all have slightly different behavior. They are all mostly built on top of the code in lib/AST/NameLookup.cpp, but there are a lot of filtering and post-processing steps on top that are composed in various ways, with many flags to control behavior, etc.

  • There is the name lookup that happens in type context (unqualified and qualified). This is how we look up types in function signatures, etc.
  • unqualified lookup in the pre-check expression pass for resolving unambiguous simple declaration references in expressions
  • the general case of unqualified and qualified lookup done by the expression type checker, which differs in behavior because of multiple code paths if there is more than one overload or not
  • the parser has its own local name lookup that's somewhat broken
  • code completion and typo correction use a "find all lookup results" implementation that is a copy and paste with edits of the primary name lookup code

Looking up associated types is a special thing that also happens in type substitution, the generic signature builder, etc.

None of this has a spec and its quite brittle with many emergent behaviors that are hard to understand and must be preserved for source compatibility.

There is an unfinished "ASTScope" implementation that tries to simplify the unqualified name lookup story somewhat and at least unify the main unqualified lookup with the parser's special parse-time lookup, but it needs more work before it can replace the old code.

6 Likes

This is an old topic, but would it be viable to revisit this proposal at some point? It sounds like there was some unfinished work holding back an implementation — is that still the case?

3 Likes

+1, I would still love to see nested protocols; it would remove so many unnecessarily verbose protocol names.

I'm curious if there would still be issues with protocols nested within generic types:

It (naively) seems possible at this point: Foo<String>.Delegate and Foo<Int>.Delegate are simply different protocols (and I would assume that the generic params from the outer type would be available within the protocol).

It's not that it isn't possible to allow this and have them be different protocols; we could do that, and it's probably the most natural way of reading that code. My concern is that this is an indirect way to get to a major feature, generic protocols, and that we shouldn't go about a major expansion to the generics system indirectly. If we're going to end up with nested protocols with these semantics, they should be a result of having generic protocols as part of the language, and have dealt directly with all of the effects of such a feature.

Doug

7 Likes

Makes sense – this particular case seems clear enough, but I would hate to see local decisions (for what is ultimately a small enhancement) force compromises down the road when generic protocols are picked up for real.

For what it's worth, that being the case: I am weakly in favor of waiting on nested protocols altogether until generic protocols can be comprehensively addressed, simply for consistency.

1 Like

All I can say to this is, "yes please". I love Swift's generics and these things from my point of view are simply holes in the language like for example missing opaque types/reverse generics or a generalization of the super-type constraint.

Can't we just allow non-generic nested protocols for now, and discuss future of generic protocols more thoroughly some time later?

I mean, in 99% cases people just want namespacing. As an example: I'm working on a library FDBSwift which has one root name — FDB, containing all other things (like Transaction, Future, Tuple etc.), and it feels completely natural to hide all other declarations under that name, including protocols (because why not) (namings like FDBTransaction and FDBFuture are ugly when you have namespaces). But in reality I still have to have top-level protocols AnyFDBKey and FDBTuplePackable just because I'm not allowed to have nested protocols, and it drives my OCD crazy :smiley:

12 Likes

Would these really need to be considered different protocols? Or could they be considered multi-parameter protocols (i.e. multi-parameter type classes) where some parameters are bound by the containing generic type?

I very much want to see both multi-parameter protocols and the ability to nest protocols in types and types in protocols. However, I think Improving the UI of Generics is a higher priority than these topics. In particular, I think the concern you express in the manifesto about people often really wanting generalized existentials when they bring up generic protocols is valid.