The review period for SE-0299: Extending Static Member Lookup in Generic Contexts has concluded. The core team has decided to return this proposal for revision. We agree that extending .leadingDot
contextual member lookup to work in generic contexts fits well with the direction of Swift, and that extending it to find members from protocol extensions corresponding to constraints on the generic context is a reasonable incremental expansion of the existing model. However, the core team is concerned about the potential for namespace pollution when using this new feature as exemplified in the proposal, an issue the review discussion also raised concerns about, and would like to see the proposal revised to address this concern.
To reiterate, the proposed feature allows an API to associate constants for different concrete conforming types with a protocol. For example, given:
protocol P {}
struct A: P {}
struct B: P {}
struct C: P {}
extension P {
static var a: A { return A() }
static var b: B { return B() }
static var c: C { return C() }
}
func useP<T: P>(_: T) {}
SE-299 would allow users to call into a generic API like useP
with shorthand syntax for these static properties:
useP(.a)
useP(.b)
useP(.c)
This effect is desirable. However, because a protocol extension extends all types conforming to the protocol, these static constants end up polluting the namespace of unrelated types, allowing for other nonsensical combinations like:
useP(B.a)
useP(A.c)
This will noise up documentation and code completion for these concrete types, running against the proposal's stated goal of improving the ergonomics and discoverability of these sorts of APIs. The core team believes this is an important problem to solve before accepting this proposal.
In the review discussion, there were three broad approaches to addressing this problem:
- Adding the ability for a protocol to specify a different type to perform contextual member lookup in
- Adding the ability to extend a protocol existential type's metatype (
P.Protocol
) instead of extending all conforming types to the protocol, and expanding the scope of contextual member lookup to also include these existential types - Adding the ability for protocol extensions to apply same-type constraints to the
Self
type
Of these approaches, the core team favors the third. There are discoverability concerns using a different type to contain contextual member lookup members; SwiftUI had tried this with its StaticMember
approach, but it proved to make constants in the StaticMember difficult for users to discover. If this were made an official language feature, tooling could conceivably be updated to recognize this new delegation pattern, but that tooling work would nonetheless need to be added to the scope of the proposal.
On the other hand, the core team acknowledges the idea of extending existential types independent of conforming types is an appealing one; it maintains discoverability of the API by associating constants directly with the protocol, without polluting the namespaces of unrelated conforming types. However, it would also mean that those constants are also not available even on the "correct" concrete type. Extending existential types would also generally be a larger expansion of the model; although the core team supports exploring extensions of structural types as an eventual direction for Swift, it's a feature that needs broader consideration.
Prior to review, both of these approaches had also been explored in the original pitch, which led to SE-299 leveraging existing protocol extensions as an incremental improvement that does not require large model changes to achieve the desired effects. The core team is supportive of this incremental approach, and we see the idea of applying same-type constraints to Self
in a protocol extension as a promising way of addressing the biggest shortcoming of this proposal while maintaining its incremental nature. Although in other contexts, an extension P where Self == ConcreteType
seems like a long-winded way of saying extension ConcreteType
, in the case of generic contextual member lookup, it expresses what we want: name lookup should be able to find these members in a context where we only know a type conforms to the protocol, but it should only actually be available when the concrete type matches the type of the member. The core team would like to see this proposal revised to include Self
same type constraints.
Thanks to everyone who participated in the review!