[swift-evolution] [RFC][Proposal] Ease restrictions on protocol nesting

Karl Wagner razielim at gmail.com
Tue Feb 7 05:51:44 CST 2017


> On 7 Feb 2017, at 06:18, Slava Pestov <spestov at apple.com> wrote:
> 
>> 
>> On Feb 6, 2017, at 9:12 PM, Karl Wagner <razielim at gmail.com <mailto:razielim at gmail.com>> wrote:
>> 
>> 
>>> On 7 Feb 2017, at 06:05, Slava Pestov <spestov at apple.com <mailto:spestov at apple.com>> wrote:
>>> 
>>>> 
>>>> On Feb 6, 2017, at 9:00 PM, Karl Wagner via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>>> - Nested protocols in generic types are not parameterised by the parent's generic parameters.
>>> So if I write GenericType<Int>.SomeProto and GenericType<String>.SomeProto, is it the same protocol? What about GenericType.SomeProto, is that allowed?
>>> 
>>> Slava
>> 
>> GenericType.SomeProto (without parameters) is the only spelling that is allowed. There is no GenericType<Int>.SomeProto.
>> 
>> That way we avoid every bound-generic type creating a new protocol. 
>> I think it works really nicely when you consider what it would like like with existential-based capturing. Notice that there is only one ‘MyCollectionView.Source’, and compatibility is determined based on existential constraints.
>> 
>> - Karl
>> 
>> 
>> class MyCollectionView<MediaItem> : UICollectionView {
>> 
>>     protocol Source {
>>         // [implicit] associatedtype MediaItem
> 
> I’m worried this is going to be tricky to implement; we definitely won’t get this with the initial implementation of nested protocol types.

Do you mean that unparameterised protocols would be tricky to implement, or that capturing and implicit associated types would be? Because the latter isn’t part of the proposal - just sketching out how a capturing solution _might_ look.


> 
> 
>>         func item(at: Int) -> MediaItem
>>         var numberOfItems: Int { get }
>>     }
>>     var source: Any<MyCollectionView.Source where .MediaItem == MediaItem> // Not possible today.
> 
> I think for this use-case, it makes sense to allow the protocol itself to be parametrized.

I used to think so, but your comments on the PR and working through some examples made me prefer this way. I think it’s exactly what we mean when we say that associated types are preferred to generic protocols. It also means that adding or removing generic types from the parent does not affect the nested protocol in any way - it could still be mangled the same for ABI compatibility and spelled the same for source compatibility.

The syntax is not exactly ideal. I’m using the Any<...> syntax because I think it’s a bit easier to understand in the context of this discussion, but that’s already gone from the language, so really it would look closer to:

var source: Source where .MediaItem == MediaItem

Still not ideal. But then, we automatically infer generic type parameters inside a generic context. Maybe it would be reasonable to do the same in this context?

extension Array where Element: Comparable {

var reverseSorted: Array {  //< inferred to be Array<T> by context
return self.sorted(by: >)
}
}

For capture-existentials, the analog may look something like:

var source: Source // [implicit] where .MediaItem == MediaItem, exhaustively binding all captured types. May be over-specific.

We would need a way to opt-out of that, though:

var source: Source where _ // Make the existential less specific with an explicit ‘where’ clause.

But none of that is part of the proposal. Again, just sketching what the situation might look like later if we took this approach. I’m curious to see what others feel about it.

- Karl

> 
> Slava
> 
>> }
>> 
>> class BookSource: MyCollectionView.Source {
>>     typealias MediaItem = Book
>> 
>>     func item(at: Int) -> Book { /* ... */ }
>>     var numberOfItems: Int     { /* ... */ }
>> }
>> 
>> class DummySource<MediaItem>: MyCollectionView.Source where MediaItem: DummyConstructable {
>>     // associatedtype 'MediaItem' bound to generic parameter.
>> 
>>     func item(at: Int) -> MediaItem { /* ... */ }
>>     var numberOfItems: Int          { /* ... */ } 
>> }
>> 
>> MyCollectionView<Book>().source = BookSource()
>> MyCollectionView<Book>().source = DummySource<Book>()
>> MyCollectionView<Song>().source  = DummySource() // type is: DummySource<Song>
>> MyCollectionView<Movie>().source = DummySource() // type is: DummySource<Movie>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170207/d73e2df0/attachment-0001.html>


More information about the swift-evolution mailing list