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?
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 thatT()
is equivalent toT.init()
for all typesT
,
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...
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)