The NaN of types (in D)

Q. Schroll qs.il.paperinik at gmail.com
Mon Oct 12 19:57:02 UTC 2020


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.)


More information about the Digitalmars-d mailing list