[swift-evolution] PITCH: New :== operator for generic constraints

Charles Srstka cocoadev at charlessoft.com
Tue Aug 16 20:40:15 CDT 2016


> On Aug 16, 2016, at 8:13 PM, Slava Pestov <spestov at apple.com> wrote:
> 
> -1 — this adds a new syntax with little gain, and potentially a lot of additional complexity.
> 
>> On Aug 16, 2016, at 2:49 PM, Charles Srstka via swift-evolution <swift-evolution at swift.org> wrote:
>> 
>> Unfortunately, when this has come up on the list in the past, it has been mentioned that there are some cases where an existential of a protocol does not conform to the protocol itself, so it is impossible to make : always match items that are typed to the protocol.
> 
> Indeed, the best solution IMHO would be to implement self-conforming protocols, so that what you’re describing can be expressed without any additional syntax.
> 
> The condition for a protocol to be able to conform to itself is the following:
> 
> - it must not have any associated type requirements, or contravariant Self in requirement signatures; eg, this rules out the following:
> 
>  protocol P { func foo(s: Self) }
> 
> - it must not have any static method or initializer requirements
> 
> With these conditions met, it would be possible to allow a generic parameter ’T : P’ to bind to a concrete type ’P’.

Well if it can be done, then that’s great. The reason I thought of a new modifier is because the last time I suggested extending : to include the protocol itself, the reaction was quite negative, suggesting that the amount of work necessary to do that would be outside the bounds of what could be considered reasonable.

I am a little concerned about the second requirement. Protocols that include static methods and initializers work perfectly well inside arrays, and restricting them from generic collections will further discourage use of the latter in favor of the former.

> Note that the type checker work required for this is not very difficult. Indeed, we already allow @objc protocols that don’t have static requirements to self-conform. The real issue is the runtime representation gets tricky, if you want to allow a generic parameter to contain both a concrete type conforming to P, and an existential of P. Basically a generic parameter is passed as three values behind the scenes, the actual value, type metadata for the concrete type, and a witness table for the conformance. To allow the parameter to be bound to an existential type we would need to pass in a special witness table that unpacks the existential and calls the witness table contained in the existential.
> 
> It’s even worse if the protocol that self-conforms is a class-bound protocol. A generic parameter conforming to a class-bound protocol is passed as a reference counted pointer and witness table. Unfortunately, a class-bound existential is *not* a reference counted pointer — it has the witness table ‘inside’ the value.
> 
> Probably my explanation isn’t great, but really what’s bothering you here isn’t a language limitation, it’s an implementation limitation — once we figure out how to represent protocol existentials efficiently in a way allowing them to self-conform, we should be able to address these use-cases without new syntax.

What I’ve long wondered is why we don’t have this problem with arrays.

protocol MyProto {
    func baz()
    
    // Includes static and initializer requirements
    static func qux()
    init()
}

struct MyStruct: MyProto {
    func baz() {
        print("baz")
    }
    
    static func qux() {
        print("qux")
    }
    
    init() {
        print("init")
    }
}

func foo(bar: [MyProto]) {
    for eachMyProto in bar {
        eachMyProto.baz()
    }
}

let x = [MyStruct()]
let y = x as [MyProto]

foo(bar: x)
foo(bar: y)

This compiles and runs fine. Why is that?

Charles

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


More information about the swift-evolution mailing list