I've been working with the @_implements attribute in some of my projects, and I've found it incredibly useful, especially when dealing with conflicting requirements from adding new conformance to an existing code.
It's very common that a protocol has a name clash with existing class/struct, e.g. protocol has a function func session() -> Session and the target class has a let session: Session.
I'm surprised this is still not an official feature, given how much flexibility it provides to protocol conformance and the fact that it was added 7 years ago ( PR #8735). Is there any particular concern or blocker preventing it from becoming official?
I would love for someone to take on this feature and make it real, but I don't think it should be spelled as an attribute. My preferred syntax is stolen from C#, where a declaration can have the name <ProtocolName>.<myname>, e.g.,
protocol P {
func foo() { ... }
}
extension S: P {
func P.foo() {
// this function foo is only meant to implement the "foo()" requirement of P
}
}
I don't think there's any ambiguity there. Inside S, the typealias P is any Dep.SomeProto, and func (any Dep.SomeProto).foo(...) would be nonsense. AFAIK, whatever introducer is used before the function's base name has to be something that the containing type conforms to, so the search space can be restricted to the inheritance tree; other names in surrounding lexical scopes can be ignored.
The main ambiguity I could see is if you have a type conforming to two protocols from different modules but with the same name, but that's trivially solved by writing a fully-qualified name:
import A
import B
struct S: A.P, B.P {
func A.P.foo() {}
func B.P.foo() {}
}
We'd need to allow that anyway since SE-0404 allowed nesting protocols in other types.
Yeah, however it's spelled this is something I would love to see come to the language officially without the need for arcane workarounds like @xwu's. For better or worse I think the existence of @_implements has acted as a bit of a pressure release valve for the urgency of solving this, but the fact that there's an existing unofficial feature for this should hopefully make the implementation path easier for any community member who wants to pick this up!
I suppose since this would be a new syntactic construction we could require that the name here always be fully-qualified to resolve the 'type with the same name as the module' issue, but it might be surprising that the lookup rules here are not the same as elsewhere.
Since I don't deal with Obj-C interop much, no idea
Which will get the job done for @pofat's use case, but still requires manually writing out a shim, whereas @_implements does not:
struct S: {
var foo: Int { 42 }
}
extension S: P {
func P::foo() -> Int { self.foo }
}
So the behavior of the attribute actually tackles the specific problem stated here in one go—namely, I need an operation which I have chosen to name foo to be invocable in the generic context by the spelling bar.
The full qualification thing is more general, but I do have some philosophical concerns whether the more generalized feature permitting the implementation of arbitrary operations that cannot be invoked on a value of concrete type directly should be utterable [edit: I guess unless you also explicitly qualify that invocation, fair enough—this would also raise a subtle distinction between (S() as any P).foo() and S().P::foo() which will be fun and exciting to explain], versus behavior that merely has a different name in a generic context. But this doesn't need to be settled for @_implements.
Basically saying how there's no guarantees regarding it's usage. Is the spirit of that message still true today, or do we have guarantees that usage of @_implements won't result in our app being liable to break if support for it is pulled in the future?
As proposed, the fully-qualified name syntax would always have a module name on the left side of the ::. In most places where we want to support ::, always interpreting the name as a module name is necessary for the feature to reference the contents of shadowed modules, which is the feature's most important use case. So func P::foo() would refer to the foo() declared in the moduleP, not the protocolP. (Which is somewhat nonsensical, so we probably wouldn't actually support it. However, if we supported func P.foo(), then func BazLib::P.foo() would make perfect sense, and would refer to the foo() of the protocol P declared in the module BazLib.)
Right, and in Tony's example we'd need the true fully-qualified syntax if A had a top-level type A, because func A.P.foo() { ... } would be implementing the requirement foo of the protocol P nested inside the type A::A and we'd have no way to refer to a top-level protocol A::P.