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

Maximilian Hünenberger m.huenenberger at me.com
Fri Jan 22 10:36:14 CST 2016

> Am 22.01.2016 um 03:05 schrieb David Waite <david at alkaline-solutions.com>:
>> 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). 

Since Hashable is only self constrained due to Equatable should we even allow this code?

let a: Equatable = 1
let b: Equatable = "2"

a == b

// in this case there could be a more general == function
func == <T: Equatable, U: Equatable>(x: T, y: U) -> Bool {
    if let yAsT = y as? T {
        return yAsT == x }
    return false

But it seems like that there has to be a different implementation of `self` constrained functions (like ==) if a self constrained protocol is used without specifying `self`.

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

What do you mean by:

> Protocols would not have a simplistic order to implement (for example, I could be extending two parent protocols, both with their own associated types)

Do you mean conforming to a protocol by "extending"?
In this case it would be even more explicit about types:

struct Numbers: SequenceType<Element: Int, SubSequence: Numbers> { ... }

To be clear, I'm proposing:
- Generic syntax for all types including protocols to be the same
- Named generic parameters eg: Array<Element: Int>  ,  CollectionType<Element: String, Index: Int>
- Without name: Array<Int>  ,  CollectionType<String, Int>
- Introduce "_" as placeholder for a covariant "any type" to make protocols and other generic types partially constrained:
Array<Element: _ >  ,  CollectionType<Element: String, Index: _ >
which is the same as
Array< _ >  ,  CollectionType<String, _ > == CollectionType<String> == CollectionType<Element: String>

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

Index: Foo
Index is of type Foo or a Subtype.

- If Foo is a struct or a enum, Index can be of type: Foo or a subtype e.g. Foo<Int> and Foo< _ >

- If Foo is a protocol or a class, Index can be of type (additionally to the ones above): subtypes e.g. Subclass<Int> , Subclass< _ > and Int if Int is of type Foo

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

That was also my concern since I want to keep the generic syntax consistent with protocols. So maybe we could make it the same in case of generic parameter names but without the "_" so there are no protocol like behaviors for generic types.

Even though it would be nice to have a covariant Array< _ > type.
Talking about covariance how about (protocol Test {} , Int conforms to Test) a covariant Array<Element: Test> to Array<Int> and an invariant Array<Test>?

- Maximilian
> -DW
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160122/bab5bb54/attachment.html>

More information about the swift-evolution mailing list