Ask HN: Should scripting engines distinguish between "null" and "undefined"?

8 points by sgbeal ↗ HN
i'm working on porting/rewriting my language-agnostic scripting engine[^1] from C to C++ and i have a small conundrum:

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 ] thread
It seems rare to make use of the distinction between null and undefined in real Javascript code. It mainly seems useful in debugging: seeing a crash with an undefined value tells you something different than a null value.
> It seems rare to make use of the distinction between null and undefined in real Javascript code.

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

When defining payloads for updates, it is really useful to be able to distinguish something like

  {"name": null, "age": 25}  // Set name to null, and age to 25
from

  {"age": 25} // Only set age to 25, leave name alone
In the former, "name" has value `null`; in the latter, the "name" field has value `undefined`

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.

This is really only true if your serialization format allows you to supply an explicit `null`, omit arbitrary properties, and has no representation corresponding to `undefined`. In practice, only JSON (and serialization formats explicitly designed to be isomorphic to JSON) exhibit these characteristics.
You can have any number of sentinel objects.
> You can have any number of sentinel objects.

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 scripting languages really need both "null" and "undefined" values?

Do programming languages really need any of these two?

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

How do you accurately model real world problemds where nulls happen without this entity?
> How do you accurately model real world problemds where nulls happen without this entity?

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.

From hi level perspective in JS for me undefined means "this variable/property hasn't been assigned any value yet".

While null - "after variable/property update we explicitly got nothing (from network, file, db etc.)

Update from OP: this conundrum has been resolved. The library in question will have native JSON support once it's ported over from its predecessor, and using JSON effectively from a high-level language requires some way of distinguishing between "property not set" and "property set to a non-value," which the undefined-vs-null distinction is a perfect fit for. It turns out that both null and undefined can coexist in the current model without either requiring a concrete value to distinguish it from the other, so the initial conundrum was not a genuine problem.

Thank you to who helped me work through this.

As much as some will proclaim neither is required, I think `null` has genuine use when representing data. It's the data-model equivalent of "no preference"; "use the default"; "automatic"; or even "not applicable". That doesn't mean every field in every object/schema should allow null, but there's definitely a use-case for it.

`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 existence: key in object, typing `key?: T`

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.

> Property existence: key in object, typing `key?: T`

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.

i would model it in compliance to typescript and sql.

undefined if property does not exist on an object.

null if property exists but value is unset.