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) { ... }