If people are worried about the conciseness of having two keywords, we could lose opaque (though I agree it is a great term) and do something like:
func foo() -> Int as Numeric //This is actually returning an Int, but the outside world only sees it as Numeric
or as people have been suggesting allowing a local name
public func foo() -> MyPrivateCollection as C:Collection where C.Element == String
Note: The type on the left could be a private type, since only the part on the right is exposed externally.
The mental model here would be: I am actually returning type X, but I am only telling the world about some of it's properties (and the world will be limited to using it in that way). This means that the part on the left side of as
can be swapped out without affecting ABI as long as everything on the right stays the same.
This could also be used with typealias:
typealias MyType = Int as Numeric
pros:
• Specifies the exact type being used without fixing it in the ABI (or exposing it "public"-ly)
• Very concise syntax
• Can be used not just for return types as shown in my last post
cons:
• Yet another magic use for as
(though I believe it shouldn't conflict with current uses since the lhs is a concrete type)
To come back to the original example, it is still a bit ugly/complex, but I think it is just as readable (if not more) than the version with the opaque keyword and _.Element:
private struct LazyCompactMapCollection<Base: Collection, Element> { ... }
extension LazyMapCollection {
public func compactMap<U>(_ transform: @escaping (Element) -> U?)
-> LazyCompactMapCollection<Base, U> as C:Collection where C.Element == U {
...
}
}
For something like an Index, where you want to be able to set things as well, you would use a TypeAlias with it:
struct MyCrazyCollection<T:Equatable>:Collection {
typealias Element = T
typealias Index = Int as Numeric //We are free to change Int to anything conforming to Numeric in the future
func index(of element: Element) -> Index //This returns Int as Numeric
func object(at index: Index) -> Element //This takes Index aka "Int as Numeric"
....
}
//We can take a returned Index (aka Int as Numeric) and use it in a parameter (potentially modifying it using Numeric methods returning Self)
let a: MyCrazyCollection<String> = ["a","b","c"]
let idx = a.index(of: "b")
let elem = a.object(at: idx)
//But we can't pass an Int
let intIdx: Int = 7
let elem = a.object(at: intIdx) //ERROR: type Int is not expected type MyCrazyCollection.Index