There is a discussion of this in the proposal. We shouldn't try to infer the underlying concrete type of Q from the bodies of both d and e; rather, we need the underlying concrete type to be specified as part of the "definition' of the type alias.
I like this direction! This feature is effectively saying "there's a type here, but I'm not going to state what it is". Along the same lines but shorter: unnamed, nameless.
Somehow, “unnamed” and “nameless” sound to me like features of the underlying type, while “anonymous” sounds like features of this use of the type. Maybe that’s just me, though.
Yeah, it wouldn't be appropriate to use this feature if the fact that d and e return the same type is important. If this were expanded out to allow for opaque typealiases, you could share an alias between these two definitions. It's hard for me to see how the standard library using opaque types for collections would affect its current level composability, since the use of separate concrete wrapper types already in practice means that you'll get different types for different computations. You can't put [x.lazy.map(...), x.lazy.filter(...)] into an array either without forcing both elements to a common concrete collection type, and you could do the same if the return types of map and filter were opaque.
Although it's true that the standard library's primitive transformations would still need to implement their wrappers, this feature would allow those to be implementation details that can change without affecting source compatibility. Furthermore, this feature is useful for functions that build on top of those primitives, since it both saves you from having to write a long composed return type, and allows you to change the implementation to something with a different return type without breaking your clients. Right now, if you change:
that's an ABI- and possibly source-breaking change. If the return type is opaque, you're guaranteed to be able to change the lazy implementation of foo without breaking binary or source.
To me, unnamed or nameless would be confusing in the contexts were you have actually given the type a name, e.g. in a typealias. Maybe it's just because the feature was introduced under the title "Opaque result types," but it seems like everyone is pretty comfortable discussing these as "opaque types", and that there's no urge to say, we're really discussing "anonymous types" (which to me, invoke the truly "anonymous" classes of Java, or structs/unions in C). The types returned as opaque results are declared elsewhere, with names, but we are blocking the true type name from view. I still believe opaque is the best terminology.
I also think that the syntax typealias usage and the inline usage should be identical, or at least as consistent with one another as possible (unless we go the route of disallowing inline definition). The issue I have with the following from @Nevin:
opaque typealias Q = MyConcreteType: A where B: C
typealias Q = opaque MyConcreteType: A where B: C
is that they both appear to be constraining the concrete type, which doesn't make a whole lot of sense. It's not wrong, since MyConcreteType indeed satisfy those constraints, but it fails to adequately express that these are actually constraints on our opaque type Q.
Have we decided to definitively disallow this? It seems like it would be useful to be able to express something like this:
Personally, it would make more sense to me to name the opaque type with a separate declaration than to invent a way to refer to another declaration's opaque return type once you need the same type in multiple places.
It would be interesting to me to see how often the desire to do that comes up in practice. Rust has had impl Trait for a while now; does anyone using Rust have any experience with whether being unable to name the impl-ed type has been a problem for them?
I think it's arguable whether "opaque result type" is the best name for this feature in general, regardless of how it's spelled in the language. The word "opaque" is already pretty heavily overloaded in the compiler and implementation model. That largely doesn't leak out into the user model unless you go peeking into the implementation or try to understand the optimizer, but it has given me pause while implementing this feature as to whether there's a clearer name for what it's trying to accomplish.
"Abstract" is perhaps a better name except for the potential confusion with abstract classes and methods (and who knows, maybe we'll add those some day to Swift; but even if we don't, we can't deny the potential for confusion).
What return type do you use if you want the result of the two convenience functions to be interchangeable?
let lazyNames = somePreference ? lazyFirstNames() : lazyLastNames()
If lazy map was using an opaque typealias as its return type, you could refer to it by name and reuse it as the return type for both of your convenience functions. I'm a bit worried that you can't do this with the current proposal since each convenience function would have to redefine its own semantically different opaque type.
That would only be possible to begin with if lazyFirstNames and lazyLastNames had the exact same chain of lazy transformations. Once the types on the branches of the conditional diverge, you lose most of the efficiency benefit of generic specialization, and so you'd probably want either force both branches to Array or sink the conditional into the chain of transformations somehow.
Doesn't that essentially defeat the fact that an opaque result type "saves you from having to write a long composed return type"? If the opaque result is truly something simple like opaque Equatable then you save a lot of effort, but even in the initial proposal we saw monster signatures like:
public func reversed() -> opaque BidirectionalCollection
where _.Element == Element
where Self: RandomAccessCollection -> _: RandomAccessCollection
where Self: MutableCollection -> _: MutableCollection {
return ReversedCollection<Self>(...)
}
Which would only expand as more conditional conformances were added. With the amount of conditional conformance in the standard library, I'm skeptical that this would make the long composed return types any simpler without having the ability to say "this method just returns the opaque result of another method.
You don't have to replicate that entire signature, only the part that's interesting to your specific API. The standard library has to be generic enough to handle all possible different clients, but most of those parameters are likely to be concretized in your specific use of the API.
Maybe the one who designed the lazy filter API would think so, or maybe it was just simpler to write it as an opaque return type without really trying to take this downside into consideration.
I do like the concept of opaque return types, but I also don't think having a couple of convenience accessors using the same filter function with a different closure is that far fetched. So for cases like this, perhaps there should be a way to write a signature this way: