Why wrapping optional existential in parenthesis?

As we prepare our Codebase toward Swift 6 we also started to explicit mark existentials with any. Though not a requirement for Swift 6.0 anymore we kind of got used to it. When it comes to optional existential it's a different story. Every time Xcodes autocompletes a protocol requirement like
func showOrderDetails(delegate: (any OrderDetailsDelegate)?) I screw up my face and remove the any along with the parenthesis. Why are they necessary? There does not seem to be an alternate meaning for any OrderDetailsDelegate? for readability I would much prefer it without the parenthesis. I looked through the different existential proposals, but couldn't find an explicit explanation on the optional syntax.

I apologize if that has been discussed before.

1 Like

? is just a syntax sugar for Optional<T>. any and some are part of the type, so parentheses make it clear that this is equivalent to Optional<any P> and not any Optional<P>, with the latter have no meaning for the type system.

That is a valid future direction. It would just need a proposal and implementation.

5 Likes

I ran into something similar looking for a way to express:

some OrderDetailsDelegate.Type

The right way (for now) to do this is:

(some OrderDetailsDelegate).Type

But in theory Swift could one day compile that without the round braces (unless there is actually a legit use-case I'm not thinking of for that syntax without round braces that currently means something distinct that needs to be supported).

There is a distinction between (any P).type and any P.type, which are not equivalent. The distinction is already not easy to explain, but a shorthand where (some P).Type means some P.Type would not help matters.

5 Likes

For me the question is, when to use one or the other? When should I use any P.Type instead of (any P).Type? To me the latter one, as well as (any P)? are the most common and clash quite a bit with the conciseness that swift strives for. Changing (any P)? to any P is also quite cumbersome and counterproductive.

I see the points in the technical explanation, not sure it should surface to the usage of swift.

Hey @vanvoorden thank you for response. I found that it is weirdly inconsistent (at least to me).

You have to write (some OrderDetailsDelegate).Type to make it work, but with any it is any OrderDetailsDelegate.Type. The parenthesis would give an error.

To my logic (assuming no associated types):
any OrderDetailsDelegate.Type = OrderDetailsDelegate.Type
With this I would assume, that any is not part of type and just defining whether it is an existential.

With that logic I would continue with:
any OrderDetailsDelegate? = OrderDetailsDelegate? and
some OrderDetailsDelegate.Type = OrderDetailsDelegate.Type

but one I should not wrap in parenthesis and the other two I need to.

Bonus question: How do I even construct an (any OrderDetailsDelegate).Type?

(any P).Type is the concrete metatype for the any P type, the same way Int.Type is the concrete metatype for Int. The only valid instance of (any P).Type is (any P).self, just like how the only valid instance of Int.Type is Int.self.

any P.Type is the existential metatype for the P protocol. It can hold any metatype that conforms to P. For example, this code compiles:

var type: any BinaryInteger.Type
type = Int.self
type = UInt.self
type = Int64.self
type = UInt8.self

because Int, UInt, Int64, and UInt8 all conform to BinaryInteger. This would not compile though:

type = (any BinaryInteger).self

because any BinaryInteger does not conform to BinaryInteger.


Before the any keyword was introduced, (any P).Type was spelled P.Protocol and any P.Type was spelled as P.Type. These alternate spellings are still valid, but I recommend against using them because they're confusing and outdated.

9 Likes