Ask HN: Should scripting engines distinguish between "null" and "undefined"?
Do scripting languages really need both "null" and "undefined" values?
i have long liked that JavaScript provides both "null" and "undefined", and have modeled my scripting languages after that since tinkering with them first became a hobby (right at 20 years ago). In the C++ rewrite, however, having both null and undefined would be slightly awkward, in exactly one of the two would require non-NULL (as in C++ nullptr) value, and that just feels odd.
The simplest solution to this is to consolidate undefined/null into the same conceptual non-value and leave it at that. The more complex solution is to make one or the other of them a full-fledged value, with its own memory address, which seems somewhat counter-intuitive. (In this project's C predecessor[^1], both null and undefined have distinct C addresses, and that works well within that framework. It works _less well_ within the rewrite's framework, where _one_ of them will fit quite nicely but the other will require some shoehorning and special-casing).
One of the inspirations for this rewrite is my recent first-time work with the Java Native Interface (JNI), which treats a Java null as a C NULL. That initially bugged me, but in practice it's more comfortable than having a distinct C value representing a Java null. In Java, however, null also fills the roll of "undefined," whereas JavaScript has both (and i rather _like_ that, as the distinction is occasionally relevant and/or interesting... but also doesn't seem game-breaking if they're combined into one concept).
What say you, dear internet, on the matter of having separate "null" and "undefined" values? Yes? No? It depends? (On what does it depend?)
PS: suggestions that it instead be rewritten in Rust will be tactfully ignored.
[^1]: the being-rewritten project is: https://fossil.wanderinghorse.net/r/cwal
16 comments
[ 0.23 ms ] story [ 46.7 ms ] threadIt is admittedly rare, but when it's useful, it's _really_ useful. It can be used, e.g., to distinguish between "property is not set" and "property has been explicitly set to a non-value (null)". That's not _often_ useful, but when it is, it _really_ is. YMMV.
Most strongly-typed mainstream backend languages don't make this distinction between "a field having value null" and "a field not being present", and this results in those languages parsing both payloads as the same. This makes API design much harder than it needs to be.
Of course, but giving a memory address to the null and/or undefined value feels (after having used JNI) really awkward. My API cannot(?) sensibly distinguish between null and undefined without giving at least one of them a distinct address. (Or maybe it can... i'll have to try that...)
Do programming languages really need any of these two?
"Need," no, but when this distinction is useful, it's really useful. There's no other way (in JS, at least) to distinguish between "no value set" and "it has been set but not to a real value."
A common example is inbound JSON data: when checking for property X, it's "undefined" if it's not set at all, and "null" if the property was set to null via JSON. JSON doesn't have "undefined," and uses "null" in that context. That distinction can be used to assign a non-value (null) to a property name which might otherwise be inherited via the prototype chain and cause confusion.
Hmmm... that example suggests, because the engine will have native JSON support, that i'll _need_ a "null" value whether or not i've also got an "undefined" value.
After having considered JSON, it seems that both null and undefined are necessary in this frame so that consumers of the json can differentiate between "property not set" and "property set to a non-value."
This framework's JSON support is still on the TODO porting list from its predecessor, so that aspect hadn't yet come up in the day-to-day development.
While null - "after variable/property update we explicitly got nothing (from network, file, db etc.)
Thank you to who helped me work through this.
`undefined` to me sounds like a language/engine internal state that ideally shouldn't be relied upon. Whether that means you treat it the same as `null`, I'm unsure of.
JS obviously has `undefined`, but I think it's rare to actually rely on it specifically as different than null; the biggest specific reliance that comes to mind is polyfilling e.g. `if (typeof window.foo === 'undefined'){...}`...; This pattern could just be inverted `typeof window.foo !== 'function'` but could also be just a simple boolean check, `if (!window.foo){...}`.
PHP has something similar with typed properties that have no initial value set - they start with a state of `uninitialized` (whereas untyped properties without an initial value start with a value of `null`). In this case it really is an internal representation that scripts can't really use - trying to fetch the value of an uninitialized property is an error in PHP.
Property no-value: undefined, typing `key: T | undefined`, a default for non-existent properties.
Null: a synthetic unnamed symbol-object-nonobject artifact, arbitrarily used for statuses like n/a, not-yet, multiple-values, #err, not-set, unknown, no-result, no-value.
JSON: got it completely wrong.
i have no idea what that's supposed to mean. Presumably you're using a TypeScript-ism?
> JSON: got it completely wrong.
Agree with it or not, it's what we have.
Having implemented several JSON parsers and reformaters, i wholeheartedly with every one of JSON's design choices, including the lack of specification about integer sizes. Douglas Crockford did it exactly right, as far as i'm concerned.
undefined if property does not exist on an object.
null if property exists but value is unset.