[Pitch] Add namespacing to associatedTypes


(Jon Hull) #1

I agree with Brent that there is a larger issue to solve, but I would still like to hear your solution, as it may inform the larger solution (and this may be a starting point to tackling the whole thing).

The thing that makes the most sense to me, off the top of my head, is to have an error that says: “T is ambiguous: Use A.T or B.T to disambiguate.”. It should actually list out the cases if that is technically possible. I could also see the argument that you should always have to specify the protocol (i.e. ‘A.T' instead of just ’T’) since that would solve post-hoc naming collisions. I would also be ok with inferring which protocol T belongs to when it is unambiguously possible.

The larger issue is what if protocols A & B both have ‘var t:T’. The interaction of these two problems needs to be considered before we can make traction on either.

It seems to me there are two cases for ‘var t:T’: either the T’s are the same or they are different. If they are the same, then everything is ok assuming the semantics are also the same, but that is a big assumption. If the T's are different, compliance would require overloading a property, which isn’t currently allowed in Swift. I think there was a proposal to have something like ‘var t:T implements A’ and then having the caller disambiguate at the call site using ‘(x as A).t’.

It starts to get complex very quickly… (but it is still design work that we have to do at some point)

Thanks,
Jon

···

Types associated with a protocol are not namespaced by the protocol, but
rather by the protocol's adopters. As such, when two protocols declare a
common associatedType, adoption of those protocols introduces undesirable
ambiguity.

Given the understandable propensity of developers to arrive at similarly
named types (T, for example), it is likely that this problem will reduce
the compatibility of packages for reasons that may not be entirely clear to
the package consumer.

Here is a demonstration of the issue. Apologies, I'm a longtime reader of
the list, but this is my first time posting, and I'm not sure how best to
format this. You may also find this example on Jira (
https://bugs.swift.org/browse/SR-1065).

protocol A {
    associatedtype T
    var aT: T { get }
}

protocol B {
    associatedtype T
    var bT: T { get }
}

T is ambiguous: "Type C does not conform to protocol 'B'."

class C: A, B {
    var aT = String()
    var bT = Int()
}

T is inferred unambiguously, compiles without error.

class C: A, B {
    var aT = String()
    var bT = String()
}

T is explicit, but problematic: "Type C does not conform to protocol 'A'."

class C: A, B {
    typealias T = Int
    var aT = String()
    var bT = Int()
}

I would greatly appreciate any advice or direction as to next steps. I have
a proposal for resolving this in mind, but it seemed premature to offer it
before finding some agreement that this was worth carrying forward.

Best regards,
Noah Blake


(Noah Blake) #2

There's been a little movement on this in the ticket that I opened. Joe
Groff suggested the following attribute:

@implements(A.T)
typealias AT = String
@implements(B.T)
typealias BT = Int

I would prefer a syntax more in line with what you suggested

typealias A.T = String
typealias B.T = Int

The advantage of the attribute approach, however, is that it may be
extended to solve various naming collisions.

When it comes to associatedtypes, I can't think of a case where the
compiler couldn't infer type from the adopter's implementation. No matter
what the syntax is, it would regularly be sugared in.

While I think there's merit to the opinion that you and Brent share (solve
this problem when solving all protocol implementation collisions), it may
be best to address this somewhat large problem through a series of
incremental changes that target its components.

The thing that makes the most sense to me, off the top of my head, is to

have an error that says: “T is ambiguous: Use A.T or B.T to disambiguate.”.

I agree that it would be best to update the error associated with this
issue.

···

On Fri, Apr 8, 2016 at 7:24 PM, Jonathan Hull <jhull@gbis.com> wrote:

I agree with Brent that there is a larger issue to solve, but I would
still like to hear your solution, as it may inform the larger solution (and
this may be a starting point to tackling the whole thing).

The thing that makes the most sense to me, off the top of my head, is to
have an error that says: “T is ambiguous: Use A.T or B.T to
disambiguate.”. It should actually list out the cases if that is
technically possible. I could also see the argument that you should always
have to specify the protocol (i.e. ‘A.T' instead of just ’T’) since that
would solve post-hoc naming collisions. I would also be ok with inferring
which protocol T belongs to when it is unambiguously possible.

The larger issue is what if protocols A & B both have ‘var t:T’. The
interaction of these two problems needs to be considered before we can make
traction on either.

It seems to me there are two cases for ‘var t:T’: either the T’s are the
same or they are different. If they are the same, then everything is ok
assuming the semantics are also the same, but that is a big assumption. If
the T's are different, compliance would require overloading a property,
which isn’t currently allowed in Swift. I think there was a proposal to
have something like ‘var t:T implements A’ and then having the caller
disambiguate at the call site using ‘(x as A).t’.

It starts to get complex very quickly… (but it is still design work that
we have to do at some point)

Thanks,
Jon

Types associated with a protocol are not namespaced by the protocol, but
rather by the protocol's adopters. As such, when two protocols declare a
common associatedType, adoption of those protocols introduces undesirable
ambiguity.

Given the understandable propensity of developers to arrive at similarly
named types (T, for example), it is likely that this problem will reduce
the compatibility of packages for reasons that may not be entirely clear to
the package consumer.

Here is a demonstration of the issue. Apologies, I'm a longtime reader of
the list, but this is my first time posting, and I'm not sure how best to
format this. You may also find this example on Jira (
https://bugs.swift.org/browse/SR-1065).

protocol A {
    associatedtype T
    var aT: T { get }
}

protocol B {
    associatedtype T
    var bT: T { get }
}

T is ambiguous: "Type C does not conform to protocol 'B'."

class C: A, B {
    var aT = String()
    var bT = Int()
}

T is inferred unambiguously, compiles without error.

class C: A, B {
    var aT = String()
    var bT = String()
}

T is explicit, but problematic: "Type C does not conform to protocol 'A'."

class C: A, B {
    typealias T = Int
    var aT = String()
    var bT = Int()
}

I would greatly appreciate any advice or direction as to next steps. I have
a proposal for resolving this in mind, but it seemed premature to offer it
before finding some agreement that this was worth carrying forward.

Best regards,
Noah Blake