Why is ()() illegal?

According to the docs, Void is a typealias for ():

typealias Void = () 

That doesn't quite make sense, because ()() and ().init() are illegal. So is this a true typealias, or is there special treatment for it?

A typealias is a semantic construct, it shouldn't be thought of as something like a preprocessor macro where the preprocessor syntactically substitutes the RHS.

That doesn't quite make sense, because ()() and ().init() are illegal

()() is parsed as trying to treat the value () as if it had the function type () -> something and trying to call that.

In Void(), Void is treated as a type, not a value. Moreover, Void by itself cannot be treated as a value -- let x = Void would be an error.

As for ().init() -- Void.init() is illegal too (and they both give similar diagnostics: "value of type '..' has no member init), so not sure why you'd expected it to work.

Makes sense

Yep, that makes sense too. I'm understanding that as calling the no-arg initializer of the type Void.

This is the part I don't understand. Assuming the form above (Void()) is an initializer call, and is valid, and assuming that T() is equivalent to T.init() for all types T, I don't understand why Void.init() is invalid. Is one of my two assumptions wrong?

()/Void is a non-nominal type, which don't support explicit initialization. I'm a little surprised you're not seeing the diagnostic tailored to that situation though.

Assuming the form above ( Void() ) is an initializer call, and is valid, and assuming that T() is equivalent to T.init() for all types T ,

The second assumption is not true, I don't have a good answer for why though. On one hand, it makes the whole treatment uniform, as you point out. On the other hand, it adds extra stuff (and multiple ways of writing the same thing) but you almost certainly never want to write that init(..) explicitly.

It would also add inconsistency in a different way -- one might ask a very reasonable question "if a non-nominal type can have an initializer method, why can't it have extension methods too?". And that's another rabbit hole... :slight_smile:

What you see on the right side of the equals sign in a typealias statement equates to seeing the same thing on the left side of the equals sign elsewhere. (Which is the same meaning as after colons and the arrow token.)

Some consequences:

You can't just use ().self, ever, and therefore might need to improvise…

let voidType: ().Type = type( of: () ).self

…but you can use () whenever a type literal is expected, i.e. where a value is not a possibility.

var void: () = voidType()
void = ()

This is the converse of not being able to just put a type name on the right side of =. The weirdness with the 0-tuple type (or n-tuples containing them) is that it's not expressible where values are, because these types' values are written exactly the same as their types.

// Compound Voids compile.
var voids: ( () , () ) = ( (), () )

// But they still can't be types, on the right side of =,
// without typealiases or metatype variables.
// Error!
voids = ( (), () )( (), () )

As for the .init issue, the 0-tuple isn't so special. Any other compound type can be made with the same syntax, and trying to use .init won't work.

let ints: (Int, Int) = (Int, Int)(1, 2)
Terms of Service

Privacy Policy

Cookie Policy