Another Trial Design
For now, no "syntactic" protocols, just extensions to protocol. They will be "optional" and "template". The first one already exists for Objective-C protocols. For the 1.0 version, use of template for user-defined protocols is banned outside the standard library, but users can conform to such protocols. The same restriction applies to optional, unless the protocol is marked as @objc, refines NSObjectProtocol, or has a class requirement of something that conforms to NSObjectProtocol (like NSObject, NSProxy, or a derived class).
The "template" declaration modifier can only appear for members of a protocol declaration, and only for methods, initializers, and subscripts. It can use the "#any" placeholder for function parameters, opaque types for generic parameters, or both.
protocol MyProtocol1 {
template init<some T>(wrapped: T, #any)
}
Types implementing the above protocol must have at least one initializer with at least one parameter, where the first parameter has the label "wrapped," and can have any number of parameters following it. The generic parameter isn't a real one, in that the implementing member doesn't need to be generic:
struct MyType1: MyProtocol1 {
init(wrapped: Int) {}
}
There can be more than one match:
struct MyType2: MyProtocol1 {
init(wrapped: Int, somethingElse: Double) {}
init(wrapped: String) {}
}
A placeholder generic can be restricted:
protocol MyProtocol2 {
template myFunc<some T: SignedNumeric>(type: T.Type) -> T
}
struct MyType3: MyProtocol2 {
myFunc(type: Int.Type) -> Int {/*...*/}
myFunc(type: String.Type) -> String {/*...*/}
myFunc() -> Bool {/*...*/}
}
String doesn't conform to the template method, but templates don't care. And they definitely don't care about the similarity named method with a Bool return.
Oh, and just because templates avoid the need for actual generics doesn't mean we can't implement a template as one:
struct MyType4: MyProtocol2 {
myFunc<U: BinaryInteger>(type: U.Type) -> U {/*...*/}
}
The versions of myFunc for a type that conforms to SignedNumeric will match the template, but any other BinaryInteger-conforming type will not (and won't cause a error).
You can also define a method with both opaque and conventional generic parameter types. The implementing methods need to be generic at least on the conventional generic parameters.
protocol MyProtocol3 {
template func myFunc2<some T: BinaryInteger, U: RandomAccessCollection>(_ x: U) -> T
}
struct MyType5 {
func myFunc2<V: Collection>(_ y: V) -> Uint { return numericCast(y.count) }
}
These protocols are restricted to the standard library for now because there no witness tables for them; only the compiler and runtime code can look for matches and/or exploit them (like for function builders and such). A template member can also be optional.