Hi, I'm really sorry for this late reply, but I think it's worth discussing.
I read through this thread, and find that what you are proposing can be divided into two parts:
- Specialized-type extension: Enabling extension of generic type with type parameter like
extension Array<Int> {}
- Generic parameterized extension: Enabling extension with generic type parameter like
extension<T> Array where Element == Optional<T> {}
I'm supportive of 1. It is really readable and writable, and maybe has few problems around ABI.
About 2, I think there is another syntax to achieve it, so I want to suggest it as an alternative. Basically, it would make implementation easier keeping the core of the feature.
Generic parameterized extension works like this.
// Currently error
extension Array where Element is Optional {}
// With parameterized extension
extension <T> Array where Element == Optional<T> {}
However, there is another possible position, after where
. I'd like to call it as 'generic where'. It works totally equal to extension <T>
. Especially, T
is usable inside extension.
// Having type parameter clause after `where`
extension Array where <T> Element == Optional<T> {}
As far as I checked examples in this thread, there are no situations that are impossible with generic where.
Merits
Consistency
It solves the concern raised here. Because it doesn't allow diverse declaration, codes would become more consistent.
// With parameterized extension
extension Array where Element: Codable { }
extension <T> Array<T> where T: Codable { }
extension <T: Codable> Array<T> { }
// With generic where, only this is allowed
extension Array where Element: Codable { }
And this is the biggest point. It rescues this. Generic where has less variation than parameterized extension. Therefore, we have less possibility to accidentally break ABI. I don't have knowledge about implementation, but I think it would make implementation easier.
Generality
Also, it can be used outside of the extension. We can use this feature anywhere in the code. This is more consistent than an extension-only feature.
// limit Element to Optional
struct OptionalArray<Element> where <T> Element == Optional<T> {}
Currently, there are small needs for generic where outside of extension, but if we introduced variadic generics referred in future direction, this feature would be more necessary outside extension. This is an instant syntax, but you can find how it can be useful.
struct Collections<...(T: Collection)> where <Element: Numeric> T.Element... == Element {}
Collections<Array<Int8>, Set<Int8>> // OK
Collections<Array<Int8>, Array<Int8>, Set<Int8>> // OK
Collections<Array<Int8>, Set<Int16>> // Error
Without generic where, we would have to write Collections
using dummy type parameter like this.
struct Collections<...(T: Collection), Element: Numeric> where T.Element... == Element {}
CertainNumericTypeVector<Array<Int8>, Set<Int8>, Int8>
Of course, now the signature of variadic generics is not fully decided. It can be unnecessary if we introduce a stronger feature like #allequal
which is referred in the pitch in 2016.
Sufficiency
One more merit of this expression is, there is no necessity to explicitly ban extension <T> T {}
, because you cannot write it from the beginning. It's sufficient for what we want to achieve.
Discussion
Redundancy
It cannot help some situations. For example, this code won't get easier to read.
// With parameterized extension
extension <T> Dictionary<String, T> {}
extension <T> Array<T?> {}
// With generic where (equal to current Swift)
extension Dictionary where Key == String {}
extension Array where<T> Element == T? {}
In addition, though generic where reduces variations, some of variations still remain like the next codes. I think we don't have to care it, because no one would make the code longer using this feature. Or, we can ban using ==
between (not variadic) associated type and bare T
in where
clause.
// With parameterized extension
extension Array where Element: Codable {}
extension <T: Codable> Array where Element == T {}
// With generic where
extension Array where Element: Codable {}
extension Array where <T: Codable> Element == T {}
Also, generic where has additional variation in function signature.
// With current Swift
func optionalArray<Element>(_ value: Array<Element?>){}
// With generic where
func optionalArray<Element>(_ value: Array<Element?>){}
func optionalArray<Element>(_ value: Array<Element>) where <T> Element == Optional<T> {}
I think it is within the range of acceptable redundancy. At least, about ABI, we don't have to specially treat these two functions to be equal, since its type parameters are different. But I admit there is room for discussion.
Visibility of type parameter
As mentioned in the earlier discussion, I think the parameter should be private in extension, generic types, and function. It would be nice if you can explicitly publish it.
struct OptionalArray<Element> where <T> Element == Optional<T> {
typealias Wrapped = T
}
OptionalArray<Int?>.Element // Int?
OptionalArray<Int?>.T // Error
OptionalArray<Int?>.Wrapped // Int
Disturb future direction
It can seem a demerit that it can't enable extension <T> T {}
in the future.
But it isn't serious. We don't even think of writing extension <T> T {}
with current Swift. What we tend to try is extension Any {}
, but after trying we find this is disallowed. So, it is more natural to lift limitations and enable extension Any {}
.
We can say a similar thing for protocol conformance. What we naturally write with current Swift is extension ProtoA: ProtoB
and not extension <T: ProtoA> T: ProtoB {}
. It is more natural to enable extension ProtoA: ProtoB
, isn't it?
Adding generic parameterized extension makes extension <T> T {}
and extension <T: ProtoA> T: ProtoB {}
natural, but we don't have to have two ways to achieve almost the same thing. We can simply lift some of the limitations and things referred in future direction can be achieved.
Therefore, it is not a problem that generic where can't enable extension <T> T {}
. It is also really easy to reject enabling extension Any {}
in the future discussion, because generic where doesn't imply even possibility to enable such a feature.
Conclusion
There are many merits to choose generic where:
- We have no needs to explicitly ban the use of
extension <T> T {}
. - We can have less variety of spellings so that expressions would be consistent across developers.
where <T>
can also be used anywhere, which will be more useful with variadic generics.
Some discussions about generic where:
- In some cases, we can't reduce redundant codes, and have new variations of the function signature.
- We must wait until
extension Any {}
andextension ProtoA: ProtoB {}
are achieved, to get the feature we expected for the future parameterized extension.
I think it can be a good alternative to generic parameterized extension in that it can solve some concerns raised in this thread. How do you think?