Why is (any Any).Type different to any Any.Type?

Why is (any Any).Type different to any Any.Type :sob:

That's it. That's the post.

  • for any concrete T.Type, it can be upcast to any Any.Type
  • (any Any).Type can be implicitly opened to some concrete T.Type

Why the assymmetry?

Is it possible to convert from one to the other (and back again)?

1 Like

any Any.Type is an existential container which can store any concrete metatype. You can also spell it as Any.Type.

(any Any).Type is specifically the type of the Any type.

Int.Type is a subtype of any Any.Type. However, Int.Type is not a subtype of (any Any).Type.

4 Likes

Thanks.

So prior to the introduction of the any keyword, there was no way to spell "the type of the Any type"?

And why the assymmetry? Like, if I have:

protocol P {}
struct S: P {}

I expect that I can both upcast an instance of S to any P, and open an existential of type any P back to a generic of type S.

But when .Type gets involved, I can upcast String.self (of type String.Type) to any Any.Type, but I can't open any Any.Type back to a generic of type String.Type. I can open (any Any).Type back to a generic of type String.Type, though. If I knew how to get an (any Any).Type representing String.self?

The "protocol metatype" of Any, or the singleton metatype of the existential type Any, was (and still can be) spelled Any.Protocol, but is additionally now spelled (any Any).Type.

The "existential metatype" which used to be spelled Any.Type can now also be spelled any Any.Type, but just as for Any the new keyword is considered optional for the existential metatype.

...existential opening also works with metatypes:

protocol P { }
struct S: P { }

func h<T: P>(_ x: T.Type) {
    print(x)
}
h(S.self) // prints "S"

func i(_ x: any P.Type) {
    h(x)
}
i(S.self) // prints "S"

Yeah, I can't explain why existential opening specifically doesn't work currently when it comes to Any.Type (and also AnyObject.Type):

func j<T>(_ x: T.Type) {
    print(x)
}
j(S.self)
func k(_ x: Any.Type) {
    j(x)
    // error: cannot convert value of type 'any Any.Type' to
    // expected argument type 'T.Type'
}
k(S.self)

Seems like a bug? cc @hborla
(At a minimum, the diagnostics that arise here are hilariously off-base, which is itself a bug.)

1 Like

is (any P).Type equivalent to P.Protocol for any protocol P?

Yeah, P.Protocol is the older syntax.

3 Likes

Was there any follow-up on this? It seems like openExistential(:do:) is the only option now, but that's supposedly not the idea.

Here is an example I have to check if a UIViewController conforms to some protocol:

extension UIViewController {
    nonisolated func conforms(to protocolType: Any.Type) -> Bool {
        func check<T>(type: T.Type) -> Bool {
            return (self is T)
        }
        return _openExistential(protocolType, do: check(type:))
    }
}

I would like to avoid using openExistential(:do:), but can't (even in the current Swift 5.7, right?)

1 Like

You should be able to just call check(protocolType) — Swift 5.7 has implicit opening of existentials.

I believe implicit opening of Any.Type is disabled until Swift 6 iirc.

1 Like
  1. I perused threads when this kind of syntax was being discussed. Are the results documented somewhere? I don't know how to write all the things now.

  2. Is there shorthand syntax coming for metatypes that will match the look of any P.type

struct S: P { }
protocol P { }
func ƒ(_ source: any P.Type) { }

…but deal with a concrete metatype instead?

func ƒ<T: P>(_ source: T.Type) { }
func ƒ(_ source: some P.Type) { } // doesn't compile
  1. Is there some way to refer to an "existential container which can store any concrete metatype", when the container is not Any?
S.self is Any // true
S.self is P   // false
  1. I perused threads when this kind of syntax was being discussed. Are the results documented somewhere? I don't know how to write all the things now.

  2. Is there shorthand syntax coming for metatypes that will match the look of any P.type

struct S: P { }
protocol P { }
func ƒ(_ source: any P.Type) { }

…but deal with a concrete metatype instead?

func ƒ<T: P>(_ source: T.Type) { }
func ƒ(_ source: some P.Type) { } // doesn't compile

Edit: Maybe it's (some P).Type? I have to think about the parens differences here.

  1. Is there some way to refer to an "existential container which can store any concrete metatype", when the container is not Any?
S.self is Any // true
S.self is P   // false

(I'm still looking for a way to replicate the old behavior.)

Unfortunately I am getting the error:
"Cannot convert value of type 'any Any.Type' to expected argument type 'T.Type'"