[swift-evolution] Partially Constrained Protocols [Was: [Proposal] Separate protocols and interfaces]

David Waite david at alkaline-solutions.com
Thu Jan 21 20:05:57 CST 2016

> On Jan 21, 2016, at 1:29 PM, Maximilian Hünenberger <m.huenenberger at me.com> wrote:
> I think we could make ConcreteThing<…> and ProtocolType<…> equal in terms of generic syntax (which could be "redefined" as I proposed). Where you can also omit generic parameters for example in dictionary:
> Dictionary<Key: _ , Value: String>
> // or short form
> Dictionary< _ , String>
> // even shorter
> [ _ : String]
> Where the Key of Dictionary is of any type which conforms to `Hashable`.
> Note: `Hashable` is self constrained and behaviors of an any type of a self constrained protocol has to be discussed.
> Second note: This is probably a bad example in terms of usability but it should only show the similar generic syntax (see my proposal).

I mentally treat self as an associated type bound to the first concrete subtype in the type chain. (first, because of class inheritance). 

Re: syntax, like I said I’m trying to defer too much syntax discussion until there is more participation/interest in the concept and approach (I think only four people have participated in this thread over nearly two weeks). 

But I’ll bite :-)

It appears you are proposing that generics be expanded to be keyed either by generic type parameter name or positionally and adding _ to positional syntax, while I imagine protocols would remain just keyed by name. Protocols would not have a simplistic order to implement (for example, I could be extending two parent protocols, both with their own associated types)

> // for a concrete index of type Int
> protocol<CollectionType where .Index == Int>
> What do you think about my proposed syntax ?
> CollectionType<Index: Int>

I personally think there is a bit too much overlap between ‘where’ syntax, and possibly conflicting usage. In your example, would Index: Foo indicate that index is exactly Foo, is Foo or some subtype, or is a concrete implementation of Foo? If there were two syntaxes, I’d hope one to be a much closer shortening of the other.

>> Finally and to a lesser degree, the parameters for a generic type define a single concrete type, while for a protocol it defines a view of some concrete type. Today I can intuit Foo<String> is the name of a concrete type. During proposal, there was more value in having a placeholder syntax that did not damage intuition about concrete types.
> What do you mean by "intuition about concrete types” ?

Today, you cannot use generics except to declare or talk about a specific concrete type with a specific implementation and behavior - you know you are dealing with a concrete, initializable Foo<Int>. Among other things, you know that the full published API for Foo<Int> is available.

From my perspective, Foo has an implicitly declared protocol. It is a protocol tied to a single implementation (or to subtypes which inherit and override that implementation, in the case of classes).

This proposal (excluding type-opening) is about being able to use a protocol with associated types more loosely. My approach is to expose the methods which can be expressed safely and uniformly based on the type constraints available, and to hide the rest. Today, you would have to do this by declaring a new protocol and using extensions to opt concrete types into it, by using generic constraints exclusively, and/or using wrapper types like AnySequence to do type erasure.

> Why not allowing Foo< _ > (to be for instance a covariant type to all Foo) ? It's similar to a protocol, but it is a more concrete type where you don't have to define a protocol to use Foo as a standalone type in a more general way. 

I do have a mild personal conflict around whether exposing the ability to have partially constrained generics is a good idea, but I admit my hesitation is based on philosophies of good application and API design, and not based on language design.

 implicit protocols on types seem pragmatic, because otherwise I’d have to create a protocol just for others to use me. 

Allowing a protocol with associated types to be partially constrained seems pragmatic to me, since otherwise I have to use one of the workarounds above (hand-coded new protocol, generic constraints ever, and/or type-erasure struct-wrapper).

Expanding the usage of the implicit protocol on some generic type seems anti-pragmatic, just because it allows someone to go longer before they really evaluate whether locking consumers of their API into a single implementation is a good idea. They can always declare a protocol explicitly if they want partially constrained behavior.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160121/9a3b84aa/attachment.html>

More information about the swift-evolution mailing list