Disclaimer: I am by no means a compiler/language expert, though I think this discussion could be useful/interesting.
In so far, this
some View
tells us a few things:
- It is a concrete type conforming to
View,
- We don't know/care what the actual concrete type is, and
- It appears only once, all other declarations may use different concrete types.
The last one hinders the ability to express relationships between types. For example, I can not express that two functions return the same opaque types. Given this example,
protocol RangeProtocol {
associatedtype Index: Comparable
var startIndex: Index
var endIndex: Index
}
struct Foo: RangeProtocol {
var startIndex: some Comparable { 0 }
var endIndex: some Comparable { 0 }
}
It doesn't work today because the compiler doesn't know the that the type of startIndex and endIndex are the same. Even if the compiler does, it does so by examining the concrete type of each property, or the fact that Foo conforms to RangeProtocol. Nothing in the declaration of startIndex and endIndex express that they should be of the same type, and at the same time opaque. This also applies to protocols with a lot of intertwined associated types like Collection.
Similar thing applies to global functions,
func foo() -> some FixedWidthType { ... }
|
func bar(_: some FixedWidthType) { ... }
There's no way to express that the return type of foo is the same as the argument of bar.
So, how can I have my cake and eat it too. I think we should add opaque type alias. Say, if I want to have an opaque type named OpaqueFoo that conforms to FixedWidthInteger and is Int underneath, I could write
public internal(opaque) typealias OpaqueFoo = opaque Int where OpaqueFoo: FixedWidthInteger
It is still opaque in a sense that, outside the opaque scope (internal), one can only treat OpaqueFoo as FixedWidthInteger. This does allow us to express the type relationship between variables while maintaining the opaqueness.
public func foo() -> OpaqueFoo { ... }
public func bar(_: OpaqueFoo) { ... }
public struct SomeCollection: Collection {
public private(opaque) typealias Index = opaque Int where Index: Comparable
public var startIndex: Index { ... }
...
}
Looking back at the some Protocol notation, it could be treated as a short hand for one-time-use opaque type alias
/// This
func foo() -> some Comparable { 4 as Int }
/// is short hand for this
typealias _Unnamed = opaque Int where _Unnamed: Comparable
func foo() -> _Unnamed
This notion also runs in parallel with generic should we add things like some Protocol that came up in Improving the UI of generics post as a one-time generic
/// This
func foo(_: some Comparable) { ... }
/// is short hand for this
func foo<T: Comparable>(_: T) { ... }