|Posted by Q. Schroll|
in reply to Stefan Koch
Posted in reply to Stefan Koch
On Monday, 12 October 2020 at 15:10:55 UTC, Stefan Koch wrote:
> You cannot create a variable of type ø.
> ø myVar; is invalid.
> You cannot ø as a returnType or as a parameter type.
> So a functiondeclatation like ø fn(int x) or int fn(ø x) is invalid.
> But you can ask for it's size.
> ø.sizeof == 0.
> For it's members
> __traits(allMembers, ø) == 
> And I think that's all of the introspection I currently support.
> __traits(getAttributes) is currently not available anymore as I need to define, a type which can hold anything (and not just types) for that to work.
As I understand it, the NaN of types (call it NaT, not a type) is only vaguely similar to NaN in the regard that is(NaT == NaT) would return false.
NaT's only purpose is to be __type.init because __type.init is needed. For that matter, __type.init could be int, but there are reasons not to do that; the same as there are reasons not to make double.init 0.0, but double.nan. Having a __type.init that, for all intents and purposes is a type, i.e. __type.init binds to type-parameters in templates, but whenever it bubbles up to the point of an `is` expression or the point where the type is used, it will be special cased so that to the user it appears as it just isn't a type. In that way, it would be totally different from a bottom type. A bottom type will always look like a type to the user. Making __type.init be __bottom is on par with making it int. To be honest, __type.init does not even need a name; it could be referred to by `__type.init` similar to `typeof(null)` hasn't a name. You could alias it, sure.
However, I'm not entirely convinced that making is(NaT == NaT) return false is a good idea. It makes things complicated and confuses the heck out of meta-programmers when debugging stuff.
Just make NaT a type. "Using" it computationally, i.e. getting its .init, declaring a variable of it, make a function return values from it, that would be an error because the type doesn't actually exist.
(Mathematically speaking, the bottom type is the empty set. You can ask if some potential element is in a type; for the bottom type, the answer is no every time. But __type.init isn't a real type. The question if something is an element of __type.init makes no sense. In that regard, __type.init kind of is like an element of the empty set: It does not exist! Still we can "let x be an element of the empty set" and talk about x. But that x cannot have a value.)
Because types must be resolved at compile-time (versus values that are resolved at run-time), double's NaN has to be dealt with in the running program. It would be great if double.init would be NaN, but the magical compiler could keep that value from ever be part of a running program rendering it unnecessary to realize NaN in the machine. However, we can do that with NaT. The programmer may have to deal with NaT on the meta-level, but the meta-resolved program *must* be free of it, otherwise it's an error. (In all the suggestions, having a bottom type meant that declaring a variable of it is equivalent to assert(0);. This is a huge difference. The bottom type is realized and this is part of its semantics.)