Allow nested protocol declarations

(Brad Hilton) #1

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

10 Likes
(Douglas Gregor) #2

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

···

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.

1 Like
(David Sweeris) #3

In your example, other types would still be able to conform to Delegate, by referencing it as "MyController.Delegate”, right? If so, I’m +1.

I’d imagine that it might also simplify the compiler a bit if we got rid of restrictions on where nested types are allowed to be declared.

- Dave Sweeris

···

On Apr 28, 2016, at 12:15 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

(Tony Allevato) #4

+1, would love to see UIKit's (and other frameworks') delegates/datasources
nested like this one day.

It seems like there's nothing that should prevent nesting like this; it's
just going to produce a different mangled name.

···

On Thu, 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.

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

(Michael Peternell) #5

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

(Krishna Kumar) #6

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

1 Like
(David Sweeris) #7

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

(Brad Hilton) #8

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

(Howard Lovatt) #9

+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.

(Howard Lovatt) #10

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.

(Adrian Zubarev) #11

+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

(Kurt Werle) #12

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/

(Frederick Pietschmann) #13

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
(Šimon Javora) #14

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

+1

2 Likes
(Douglas Gregor) #15

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

(👑🦆) #16

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
(Adrian Zubarev) #17

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):

(👑🦆) #18

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.

(Slava Pestov) #19

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
(Adam Shin) #20

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