Opaque result types with PATs

I'm trying to use an opaque PAT:

func opaqueCollection() -> some Collection {
    [2]
}
let collection = opaqueCollection()
print(collection[0])

This fails to compile with

error: no exact matches in call to subscript; Candidate expects value of type '(some Collection).Index' for parameter #1

This confuses me for a couple of reasons. SE-0244 says

If the opaque type exposes associated types, those associated types' identities are also maintained. This allows the full API of protocols like the Collection family to be used

This suggests that a) Collection.Index == Int ought to be preserved through the veil, and b) we ought to be able to use "the full API" for Collection, but neither seem really true.

I notice that the codesnippet in SE-0244 (which works) is designed to prove that the type for Element is known at the callsite, but it carefully avoids relying on the type of Index:

c[c.startIndex] = c.first!

Is there something unique about Index here that causes Element to be known but not Index?

Here's a related situation with Combine. The idea here is to have a contract for a useful Publisher, but not its exact type:

import Combine
func publisher() -> some Publisher {
    Just(2)
}
publisher().sink(receiveValue: { print($0)})

error: referencing instance method 'sink(receiveValue:)' on 'Publisher' requires the types '(some Publisher).Failure' and 'Never' be equivalent

Of course they are equivalent in this case, but we seem to have forgotten that associatedtype along the way. And without knowledge of that associatedtype, it seems like no opaque Publisher would be useful.

Side note, I'm aware most people solve this by erasing to an AnyCollection/AnyPublisher rather than using opaque types. But paradoxically, the Any wrappers are themselves some particular struct, so if the goal is to erase the return type itself (for example to allow it to change in an ABI-compatible way) returning a wrapper is in a certain sense the opposite.

You're not supposed to know that the return type (let's call it C) has Int index because it's not part of the return signature (and there's no way to add that right now). As C is opaque, its associated types like C.Element and C.Index are also opaque.

The code c[c.startIndex] = c.first! works because the types match regardless of what C actually is.


Note that, because of that, opaque return type right now isn't exactly useful for types that require to specify their associated types, like Publisher (you can't constraint Publisher.Output).

2 Likes

This doesn't prove that the type for Element is known, note that you can't write

c[c.startIndex] = 1

You know that (some Collection).Index == (some Collection).Index and (some Collection).Element == (some Collection).Element but nothing more. At least until there's some way to constrain the opaque type further, e.g. state that opaqueCollection() returns some collection with Int indices.

Terms of Service

Privacy Policy

Cookie Policy