<p>Javascript for example has both <code>null</code> and <code>undefined</code> values. They are nearly equivalent: both are falsy values that denote absence of data, the only difference is apparently whether or not it was &quot;intentional&quot;. Uninitialized variables contain <code>undefined</code> but the developer could explicitly set them to <code>null</code> to denote that it was intentional and not a mistake.</p>
<p>Is this useful? Is this a good idea for a dynamic language? Or is it enough to have just <code>null</code>?</p>

<language-design><types><nullability><dynamic-typing>
Should a language have both null and undefined values?
CC BY-SA 4.0
2024-12-24T12:19:40.320Z
FWIW, I suspect that `null` was added to Javascript to satisfy the bosses who wanted it to be more like Java. The language lets programmers write `x - y` without caring whether `y` is a number or a numeric string, so it seems implausible the design intention was for programmers to care about something much subtler.
kaya3
CC BY-SA 4.0
2024-12-24T12:36:34.140Z
And FWIW, SQL has null, which sometimes means missing and not applicable, and other times means missing though applicable.  The former is meant to suggest that the type of a row is different, as happens when you have a table of employees with a column referring to their manager, since the CEO doesn't have a manager, then her manager is null, there meaning missing an not applicable.  Whereas the latter usage of null means merely we didn't collect that information (like phone number or address).  So, there's two usages of null yet there's no way of dynamically distinguishing between the them.
Erik Eidt
CC BY-SA 4.0
2024-12-24T13:11:27.347Z
In addition, NULL is also used in SQL to mean an empty/nothing value, i.e. the value is not missing, and it would be valid for a non-empty value to exist there, but the value happens to be empty/nothing. For example, a "products" table with a column holding an optional foreign key into the "discount codes" table could have NULL meaning that this product happens to have no discount code.
kaya3
CC BY-SA 4.0
2024-12-24T14:11:35.977Z
@kaya3 Products and CEO seem to be similar case, except perhaps for the distinction of whether that column can have other values _for this row_. Maybe one way to separate those 2 NULL uses is to ask "how many?". Then for product discount and CEO's manager it's 0. But for phone or address is "I don't know".
Pablo H
CC BY-SA 4.0
2024-12-24T22:02:17.823Z
See [three-valued logics](https://en.wikipedia.org/wiki/Three-valued_logic) and [four-valued logics](https://en.wikipedia.org/wiki/Four-valued_logic).
shea
CC BY-SA 4.0
2024-12-25T09:29:57.667Z
I feel like the general rule is: if you see it in JavaScript and not elsewhere it was probably a bad idea. JS is a very poorly designed language.
Jack Aidley
CC BY-SA 4.0
2024-12-25T10:41:05.170Z
A language should have neither. Use option types instead.
user76284
CC BY-SA 4.0
2024-12-25T11:23:03.703Z
It can be useful for verification. VHDL goes a step further, their `std_logic` type has nine possible values, these often come in handy during debugging.
Simon Richter
CC BY-SA 4.0
2024-12-25T15:15:43.143Z
It would be good if it was consistent, but you explicitly assign undefined and it's not the same as an actual missing field.
riv
CC BY-SA 4.0
2024-12-25T15:21:56.963Z
Another approach beyond option types is some form of polymorphism, so that the relevant column/field for missing and not applicable items is itself absent (then no need for null in that column).
Erik Eidt
CC BY-SA 4.0
2024-12-25T18:39:33.570Z
What about `NaN` is that another type of null?
Jasen
CC BY-SA 4.0
2024-12-25T19:04:43.773Z
SAS is designed to work with survey data and has multiple null values, so you can have one that means 'not filled in' and one that means 'illegible', etc.
Hans Kilian
CC BY-SA 4.0
2024-12-27T18:16:57.470Z
According to the zero-one-infinity principle, no. Why commit just *two* [billion-dollar mistakes](https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare/) in your language?
user3840170
CC BY-SA 4.0
2024-12-29T01:56:06.033Z
Classic Visual Basic had `Null` (database semantics), `Nothing` (null reference) and `Empty` (unassigned variable) and the runtime also had a "missing" special value for an unassigned formal parameter. It was confusing! But also sometimes useful. See my 2003 article if you want more details: https://ericlippert.com/2003/09/30/a-whole-lot-of-nothing/
Eric Lippert
<blockquote>
<p>both are falsy values that denote absence of data, the only difference is apparently whether or not it was &quot;intentional&quot;</p>
</blockquote>
<p>The main problem with Javascript having both <code>null</code> and <code>undefined</code> is that they are used inconsistently, even in the standard library.  There is no absolute authority which says what each should be used to mean or indicate, and so different programmers imbue them with different meanings. Library code mostly has to treat them the same way, because there is no agreed standard for how they should be treated differently.</p>
<p>Perhaps to you, <code>null</code> means an &quot;intentionally&quot; missing value and <code>undefined</code> means an &quot;unintentionally&quot; missing value, but this isn't universal. The <a href="https://tc39.es/ecma262/multipage/ecmascript-data-types-and-values.html#sec-ecmascript-language-types-undefined-type" rel="noreferrer">ECMAScript specification</a> doesn't seem to have anything to say on the subject. <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/null" rel="noreferrer">MDN advises</a> that <code>null</code> means <em>&quot;the intentional absence of any object value&quot;</em> but <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined" rel="noreferrer">doesn't give the opposite advice</a> for <code>undefined</code>. And as an example from the standard library, <code>canvas.getContext('2d')</code> returns <code>null</code> when the same canvas is already in use in a 3D context, something very unlikely to be done intentionally.</p>
<p>Worse, &quot;intentionally missing&quot; isn't as clear a distinction as you might think. For example, function parameters take the value <code>undefined</code> when no argument is provided, but an argument could be missing either intentionally or unintentionally. Likewise, <code>map.get(missingKey)</code> returns <code>undefined</code>, but the key could be absent either intentionally or unintentionally. (Meanwhile, <code>localStorage.getItem</code> returns <code>null</code> instead in the same case.) The intent where a value is produced may also be different than where it is used.</p>
<p>As a result, if you come across some variable which might hold an object, or <code>null</code>, or <code>undefined</code>, you will have to read the comments (hopefully there are some!) to figure out what the author intended by that. Perhaps the author didn't even intend to treat them differently, just there is a mix of <code>undefined</code> and <code>null</code> because those values came from different places and there was no need to unify them.</p>
<p>If instead there was a language-wide authoritative standard which said <code>undefined</code> means one thing and <code>null</code> means another, then this would be less of a problem, although there would still be code which doesn't follow the standard. That said, if you <em>can</em> think of a clear difference between what these two values should mean in your language, then you can probably think of better names for these values which would communicate those meanings.</p>

CC BY-SA 4.0
2024-12-24T11:34:24.007Z
2024-12-24T13:53:37.217Z
2024-12-24T13:53:37.217Z
kaya3
CC BY-SA 4.0
2024-12-24T21:35:01.680Z
Nitpick: `canvas` and `localStorage` are not part of the ECMAScript standard library. They are part of the HTML5 Web API, which is a completely different library, defined by a completely different standards organization, and is language-independent, i.e., not related to ECMAScript. So, there should not be any expectation that its ECMAScript bindings are idiomatic – Language bindings of language-independent API specifications generally aren't.
Jörg W Mittag
CC BY-SA 4.0
2024-12-24T21:42:40.963Z
In particular, WebIDL, the language Web APIs are specified in, doesn't have a `null` type like ECMAScript, instead, it has nullable types `T?`. And while it has a type called `undefined`, WebIDL's  `undefined` is different from ECMAScript's `undefined` – WebIDL's used to be called `void` and is closer to Java's `void` than ECMAScript's `undefined`.
Jörg W Mittag
CC BY-SA 4.0
2024-12-26T03:50:54.650Z
I think reading "intentional absence of any object value" as giving a contrast of "intentional" against `undefined`'s description focuses on the wrong place; the key contrast is in "object", because `null` *is* an object value (has the object type, i.e. `typeof null == 'object'), and `undefined` is not (type undefined), which is somewhat significant in JavaScript's type system, so they are not interchangeable. This reading is in the question but could be rebutted here.
Michael Homer
CC BY-SA 4.0
2024-12-27T08:51:43.977Z
I think the `null` being “intentional” is less of an authoritative prescription than an observation of an emergent property of the design. A more precise statement would be that the undefined value is sometimes implicitly conjured when referring to something that fails to have a value entirely (such as non-existent object properties, unassigned variable bindings or absent keys in a `Map`), but `null` is never conjured in this way, and can only result when code is specifically written to that effect.
user3840170
CC BY-SA 4.0
2024-12-27T09:04:55.737Z
@JörgWMittag WHATWG APIs are hardly language-independent at all. They assume ECMAScript semantics in their definitions. They are JS-centric in much the same way as JVM is Java-centric and .NET is C♯-centric, and arguably to an even larger extent.
user3840170
<p>It is useful but not necessary and there are better alternatives.</p>
<p>Dynamically typed languages have the limitations that they can only discover concrete types of values. That means they cannot differentiate between <em>levels</em> of a value being undefined: a function taking a <code>value: Option[T] = None</code> cannot find out whether it received the default <code>None</code> or was passed one if <code>T</code> is itself some <code>Option[T']</code>.</p>
<p>This can be practically fixed by having more than one &quot;not defined&quot; value: A function taking a <code>value: T = undef</code> can trivially separate the case of default <code>undef</code> and passed in <code>None</code>.<br />
Of course this only shifts the problem: While for many practical problems eliminating half the collisions is enough, collisions still occur. A function that has to operate on both default and passed in <code>undef</code>/<code>None</code> still cannot separate the cases.</p>
<p>A more thorough approach is to allow/encourage defining new &quot;not defined&quot; values/types for each use. For example, Python has the idiom of using a hidden, bare <code>object()</code> as needed.</p>
<pre class="lang-none prettyprint-override"><code># placeholder with unique identity
_no_default = object()

def find(predicate, values, default = _no_default):
   ...
</code></pre>
<p>Of course a language can provide tools and syntactic sugar to make this simpler.</p>

CC BY-SA 4.0
2024-12-24T07:57:07.547Z
2024-12-24T09:58:52.177Z
2024-12-24T09:58:52.177Z
MisterMiyagi
CC BY-SA 4.0
2024-12-24T21:47:14.983Z
In Ruby, default arguments are re-evaluated on each call, unlike Python's which are evaluated only once. So, a very similar pattern in Ruby is something like `def find(predicate, values, default = (no_argument_passed = true; nil))`. This will assign `true` to `no_argument_passed` IFF the default argument was evaluated, i.e., no argument was passed. If an argument is passed, `no_argument_passed` will be defined but not initialized, which means it will evaluate to `nil`, which is falsy. The Rubinius Ruby implementation had an internal, unspeakable `undefined` value for this use case.
Jörg W Mittag
CC BY-SA 4.0
2024-12-24T21:48:44.283Z
In Rubinius, large parts of the core library were written in Ruby, but there are many methods in the core library which have semantics that are hard to express in Ruby. (A vestige of there being no specification and only a single implementation with most of the core library written in C with privileged access to the interpreter internals.) Being able to reliably determine the number of arguments passed is one of those hard to express semantics – effectively, many methods in the Ruby core library are overloaded on arity, which is not easily accessible from Ruby itself.
Jörg W Mittag
<p>It's a little different than your</p>
<blockquote>
<p>difference is apparently whether or not it was &quot;intentional&quot;</p>
</blockquote>
<p>characterization, but R has a somewhat unique implementation with <code>NULL</code> and <code>NA</code> that addresses the same fundamental issue, where <code>NULL</code> represents an undefined value (internally, it points to a singleton global constant and is effectively treated as its own unique type) and <code>NA</code> represents a scalar value that is missing or unknown. A key thing is that <code>NA</code> values in R are type-aware (there's an internal <code>NA</code> constant for each type) and can be used in place of scalar values; they will even propagate through calculations. They can be thought of as a convenient wrapper around sentinel values and have well-defined rules distinct from <code>NULL</code>.</p>
<p>For reference, I believe the <code>pandas</code> library in Python has implemented something similar to R's <code>NA</code> (specifically through <code>pandas.NA</code>), and Julia (a much newer language) has <code>missing</code> as an <code>NA</code> analog. I don't know the details of these, so they might be interesting to look into as well.</p>
<p>Here's an example where I create a vector and try replacing values with <code>NA</code> and <code>NULL</code>:</p>
<pre><code>vec = 1:5
vec
# [1] 1 2 3 4 5

vec[2] = NA
vec
# [1]  1 NA  3  4  5

vec[4] = NULL
# Error in vec[4] = NULL : replacement has length zero
vec
# [1]  1 NA  3  4  5
</code></pre>
<p>The <code>NULL</code> assignment failed because I couldn't put something of zero length into a slot that required something of length 1.</p>
<p>If I try to create a vector with each, <code>NA</code> is retained, <code>NULL</code> is not:</p>
<pre><code>c(1, NA, 3, NA, 5)
# [1]  1 NA  3 NA  5

c(1, NULL, 3, NULL, 5)
# [1] 1 3 5
</code></pre>
<p>And with math, <code>NA</code> values are preserved:</p>
<pre><code>vec + 3
# [1]  4 NA  6  7  8
</code></pre>
<p>They help distinguish between optional unknown inputs and required unknown inputs in defining functions:</p>
<pre><code>f = function(a = NA, b) { # equivalent to b = NULL as a default
  print(a)
  print(b)
}

f(a = 1)
# Error in print(b) : argument &quot;b&quot; is missing, with no default

f(b = 2)
# [1] NA
# [1] 2
</code></pre>
<p>Thinking about return values from functions, getting <code>NA</code> as a result indicates that there is a value; you just don't know what it is:</p>
<pre><code>mean(vec)
# [1] NA
</code></pre>
<p>Whereas <code>NULL</code> indicates that nothing was returned from a function.</p>
<p>Now, that doesn't mean the design is perfect and all the rules are intuitive, far from it. Keep in mind, though, that R is a statistical language that relies heavily on vector and data table structures in applications where missing data can be common. The types of language design decisions that best improve the daily experience of a statistician using R vs. a web developer using JavaScript are going to be different.</p>

CC BY-SA 4.0
2024-12-24T18:04:33.123Z
2024-12-24T18:04:33.123Z
anjama
CC BY-SA 4.0
2024-12-25T10:09:20.930Z
I like that you mention R but I don't think its rules are consistent. As you show, assigning `NULL` to an element of a vector raises an error but creating a vector with `c(1, NULL)` doesn't. Why should that be? Also `list(a=NULL)` is different to `l=list(a=1);l[[1]]=NULL` or `l=list(a=1);l$a=NULL`, which doesn't make sense to me. Additionally, `TRUE || NULL` is `TRUE` while `FALSE || NULL` raises an error (admittedly because the RHS isn't evaluated in the first case rather than directly related to `NULL` - but it can cause surprising errors). And let's not even get into `NaN`.
SamR
CC BY-SA 4.0
2024-12-25T20:45:39.330Z
The typical NULL usuage would be to make an empty vector or list and then to add elements to it. If you have a NULL vector and concatenate the number 3 to it, afterwards you have a vector of length 1 with the value 3. If instead you start with a vector NA, it will have length 1 and if you concatenate the number 3 to it, you will get a vector of length 2 containing (NA, 3).
quarague
CC BY-SA 4.0
2024-12-25T20:48:03.067Z
@SamR Yeah, by no means do I think all of the R's rules make sense, at least not without more context of the reasoning behind those decisions. Part of the reason I mentioned Julia is because it might be interesting to see what it does differently from R as a newer language with the benefit of hindsight. I just haven't done much with Julia to be able to say.
anjama
CC BY-SA 4.0
2024-12-25T20:52:23.587Z
@SamR A single element of a vector has length 1 so you can't put NULL there. c(..) on the other hand, concatenates its arguments and each argument can already have a length, so you can put numbers in there (length 1), vectors (length >=1) or NULL (length 0).
quarague
CC BY-SA 4.0
2024-12-25T21:00:41.797Z
@quarague I think it's less about how it works and more about the why. It is reasonable to ask why the creators did that instead of treating it as an error. I suspect there are pros and cons to both ways. Likewise, instead of making assigning NULL to an existing vector element an error, they could have made it remove the element instead. Maybe there are good reasons from a user standpoint for all their decisions. But I also wouldn't be surprised if some decisions were based on implementation complexity, performance, or weird interactions with other things.
anjama
CC BY-SA 4.0
2024-12-25T22:33:30.787Z
@quarague I suppose there's a logic to that but it's not the only way to do concatenation. In Python `[1] + [None]` is not the same as `[1] + []` and intuitively that makes more sense to me. In any case it still doesn't seem consistent that `l=list(NULL); l[[1]]=NULL` creates a list of length one and then a list of length zero.
SamR
<p>If the language doesn't provide this, programmers who need the distinction will end up implementing it themselves, perhaps using a clumsy structure like:</p>
<pre><code>struct optional_or_null&lt;T&gt; {
  enum {
    UNDEFINED;
    NULL;
    DEFINED;
  } flag;
  T value;
}
</code></pre>
<p>Putting it in the language definition means we don't get myriad, incompatible definitions like this. This then paves the way for common programming conventions so we use the built-in <code>null</code> vs. <code>undefined</code> distinction consistently.</p>
<p>Of course, conventions don't force this consistency, but it's still better than everyone rolling their own designs.</p>
<p>We may also be able to do better in language designs. JavaScript allows you to assign <code>undefined</code> explicitly. If <code>undefined</code> is supposed to represent &quot;not initialized&quot;, this seems to contradict it -- you can't initialize something to &quot;not initialized&quot;. Ideally we should only be able to test whether a variable equals <code>undefined</code>, not pass it around as a first-class value.</p>
<p>On the other hand, second-class values raise their own problems. If you have a function that compares values, it's frustrating if you can't pass <code>undefined</code> as the value to compare with.</p>

CC BY-SA 4.0
2024-12-24T15:59:32.063Z
2024-12-24T23:23:32.603Z
2024-12-24T23:23:32.603Z
Barmar
CC BY-SA 4.0
2024-12-27T13:03:40.340Z
This seems to assume that two undefined values is exactly what all those downstream users will need, which is the very assumption questioned here.
user3840170
<p>I propose to look at how missing values are dealt with in other fields, specifically, databases.</p>
<p>The SQL standard provides <code>NULL</code> value. But for specific use cases, Ralph Kimball <a href="https://www.kimballgroup.com/data-warehouse-business-intelligence-resources/kimball-techniques/dimensional-modeling-techniques/null-dimension-attribute/" rel="nofollow noreferrer">recommends</a> avoiding using it and providing meaningful not-NULL values instead.</p>
<p>Following this recommendation, in my recent project I had 8 different replacements for <code>NULL</code> values, each signaling the reason why the data are missing: <code>all</code>, <code>explicit empty</code>, <code>not applicable</code>, <code>invalid</code>, <code>multiple</code>, <code>not yet created</code> etc. Separate values primarily and greatly simplify debugging. But some business logic could also rely on them.</p>
<p>So, there are more criteria to use null than just 'whether or not it was &quot;intentional&quot;'. Two values aren't enough to cover all use cases. And that's why I think having both <code>null</code> and <code>undefined</code> is a useless complication.</p>
<p>I recommend this:</p>
<ol>
<li>Add just <code>null</code> for 'not intentional'. Or forbid uninitialized variables</li>
<li>For 'intentional' cases, consider adding a separate type/class/construction, with the ability to provide and get additional info: the reason why it's empty and maybe other</li>
</ol>
<p>Note that #2 may be not the part of the language core, but provided by a library.</p>

CC BY-SA 4.0
2024-12-25T23:37:42.853Z
2024-12-25T23:45:20.857Z
2024-12-25T23:45:20.857Z
Basil Peace
CC BY-SA 4.0
2024-12-26T01:04:15.317Z
"Note that #2 may be not the part of the language core, but provided by a library." - unless the library as such is defined to be part of the language core.?
ghellquist
CC BY-SA 4.0
2024-12-26T01:56:03.493Z
@ghellquist, I don't quite understand your question. By a library, I meant something like optional-js. But it should be maintained by the language creator, not a side developer
Basil Peace
CC BY-SA 4.0
2024-12-26T01:59:34.637Z
And if you imply that a language may not have a concept of library (package, plugin, etc.) at all, IMO, it can't be used for complex programs, and this whole question is not relevant
Basil Peace
CC BY-SA 4.0
2024-12-26T08:56:02.977Z
the point is that how to implement language features may differ. This means that specific library functions may be part of language core definitions and hence not optional.
ghellquist
<p>If you find this feature essential, you should probably add discriminated unions instead, as that covers what you really need to do and more. You want to have a type that is either an X, an unknown X and an inapplicable X (and probably more).</p>
<p>A problem you may encounter is that your users will also need their data store to support this, if it doesn’t then it will be of limited value (for nulls).</p>

CC BY-SA 4.0
2024-12-26T19:47:50.547Z
2024-12-26T19:47:50.547Z
jmoreno
# Should a language have both null and undefined values?
Posted by: username_0 on 2024-12-24
Javascript for example has both `null` and `undefined` values. They are nearly equivalent: both are falsy values that denote absence of data, the only difference is apparently whether or not it was "intentional". Uninitialized variables contain `undefined` but the developer could explicitly set them to `null` to denote that it was intentional and not a mistake.

Is this useful? Is this a good idea for a dynamic language? Or is it enough to have just `null`?
**Tags:** language-design types nullability dynamic-typing
> **Comments:**
> **username_1**: FWIW, I suspect that `null` was added to Javascript to satisfy the bosses who wanted it to be more like Java. The language lets programmers write `x - y` without caring whether `y` is a number or a numeric string, so it seems implausible the design intention was for programmers to care about something much subtler.  
> **username_2**: And FWIW, SQL has null, which sometimes means missing and not applicable, and other times means missing though applicable.  The former is meant to suggest that the type of a row is different, as happens when you have a table of employees with a column referring to their manager, since the CEO doesn't have a manager, then her manager is null, there meaning missing an not applicable.  Whereas the latter usage of null means merely we didn't collect that information (like phone number or address).  So, there's two usages of null yet there's no way of dynamically distinguishing between the them.  
> **username_1**: In addition, NULL is also used in SQL to mean an empty/nothing value, i.e. the value is not missing, and it would be valid for a non-empty value to exist there, but the value happens to be empty/nothing. For example, a "products" table with a column holding an optional foreign key into the "discount codes" table could have NULL meaning that this product happens to have no discount code.  
> **username_3**: @kaya3 Products and CEO seem to be similar case, except perhaps for the distinction of whether that column can have other values _for this row_. Maybe one way to separate those 2 NULL uses is to ask "how many?". Then for product discount and CEO's manager it's 0. But for phone or address is "I don't know".  
> **username_4**: See [three-valued logics](https://en.wikipedia.org/wiki/Three-valued_logic) and [four-valued logics](https://en.wikipedia.org/wiki/Four-valued_logic).  
> **username_5**: I feel like the general rule is: if you see it in JavaScript and not elsewhere it was probably a bad idea. JS is a very poorly designed language.  
> **username_6**: A language should have neither. Use option types instead.  
> **username_7**: It can be useful for verification. VHDL goes a step further, their `std_logic` type has nine possible values, these often come in handy during debugging.  
> **username_8**: It would be good if it was consistent, but you explicitly assign undefined and it's not the same as an actual missing field.  
> **username_2**: Another approach beyond option types is some form of polymorphism, so that the relevant column/field for missing and not applicable items is itself absent (then no need for null in that column).  
> **username_9**: What about `NaN` is that another type of null?  
> **username_10**: SAS is designed to work with survey data and has multiple null values, so you can have one that means 'not filled in' and one that means 'illegible', etc.  
> **username_11**: According to the zero-one-infinity principle, no. Why commit just *two* [billion-dollar mistakes](https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare/) in your language?  
> **username_12**: Classic Visual Basic had `Null` (database semantics), `Nothing` (null reference) and `Empty` (unassigned variable) and the runtime also had a "missing" special value for an unassigned formal parameter. It was confusing! But also sometimes useful. See my 2003 article if you want more details: https://ericlippert.com/2003/09/30/a-whole-lot-of-nothing/  
---
## 6 answers
### Answer by username_1 on 2024-12-24. Score: 18
> both are falsy values that denote absence of data, the only difference is apparently whether or not it was "intentional"

The main problem with Javascript having both `null` and `undefined` is that they are used inconsistently, even in the standard library. There is no absolute authority which says what each should be used to mean or indicate, and so different programmers imbue them with different meanings. Library code mostly has to treat them the same way, because there is no agreed standard for how they should be treated differently.

Perhaps to you, `null` means an "intentionally" missing value and `undefined` means an "unintentionally" missing value, but this isn't universal. The [ECMAScript specification](https://tc39.es/ecma262/multipage/ecmascript-data-types-and-values.html#sec-ecmascript-language-types-undefined-type) doesn't seem to have anything to say on the subject. [MDN advises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/null) that `null` means *"the intentional absence of any object value"* but [doesn't give the opposite advice](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined) for `undefined`. And as an example from the standard library, `canvas.getContext('2d')` returns `null` when the same canvas is already in use in a 3D context, something very unlikely to be done intentionally.

Worse, "intentionally missing" isn't as clear a distinction as you might think. For example, function parameters take the value `undefined` when no argument is provided, but an argument could be missing either intentionally or unintentionally. Likewise, `map.get(missingKey)` returns `undefined`, but the key could be absent either intentionally or unintentionally. (Meanwhile, `localStorage.getItem` returns `null` instead in the same case.) The intent where a value is produced may also be different than where it is used.

As a result, if you come across some variable which might hold an object, or `null`, or `undefined`, you will have to read the comments (hopefully there are some!) to figure out what the author intended by that. Perhaps the author didn't even intend to treat them differently, just there is a mix of `undefined` and `null` because those values came from different places and there was no need to unify them.

If instead there was a language-wide authoritative standard which said `undefined` means one thing and `null` means another, then this would be less of a problem, although there would still be code which doesn't follow the standard. That said, if you *can* think of a clear difference between what these two values should mean in your language, then you can probably think of better names for these values which would communicate those meanings.
> **Comments:**
> **username_13** (2024-12-24): Nitpick: `canvas` and `localStorage` are not part of the ECMAScript standard library. They are part of the HTML5 Web API, which is a completely different library, defined by a completely different standards organization, and is language-independent, i.e., not related to ECMAScript. So, there should not be any expectation that its ECMAScript bindings are idiomatic – Language bindings of language-independent API specifications generally aren't.  
> **username_13** (2024-12-24): In particular, WebIDL, the language Web APIs are specified in, doesn't have a `null` type like ECMAScript, instead, it has nullable types `T?`. And while it has a type called `undefined`, WebIDL's  `undefined` is different from ECMAScript's `undefined` – WebIDL's used to be called `void` and is closer to Java's `void` than ECMAScript's `undefined`.  
> **username_14** (2024-12-26): I think reading "intentional absence of any object value" as giving a contrast of "intentional" against `undefined`'s description focuses on the wrong place; the key contrast is in "object", because `null` *is* an object value (has the object type, i.e. `typeof null == 'object'), and `undefined` is not (type undefined), which is somewhat significant in JavaScript's type system, so they are not interchangeable. This reading is in the question but could be rebutted here.  
> **username_11** (2024-12-27): I think the `null` being “intentional” is less of an authoritative prescription than an observation of an emergent property of the design. A more precise statement would be that the undefined value is sometimes implicitly conjured when referring to something that fails to have a value entirely (such as non-existent object properties, unassigned variable bindings or absent keys in a `Map`), but `null` is never conjured in this way, and can only result when code is specifically written to that effect.  
> **username_11** (2024-12-27): @JörgWMittag WHATWG APIs are hardly language-independent at all. They assume ECMAScript semantics in their definitions. They are JS-centric in much the same way as JVM is Java-centric and .NET is C♯-centric, and arguably to an even larger extent.  
---
### Answer by username_15 on 2024-12-24. Score: 5
It is useful but not necessary and there are better alternatives.
Dynamically typed languages have the limitations that they can only discover concrete types of values. That means they cannot differentiate between levels of a value being undefined: a function taking a value: Option[T] = None cannot find out whether it received the default None or was passed one if T is itself some Option[T'].
This can be practically fixed by having more than one "not defined" value: A function taking a value: T = undef can trivially separate the case of default undef and passed in None.
Of course this only shifts the problem: While for many practical problems eliminating half the collisions is enough, collisions still occur. A function that has to operate on both default and passed in undef/None still cannot separate the cases.
A more thorough approach is to allow/encourage defining new "not defined" values/types for each use. For example, Python has the idiom of using a hidden, bare object() as needed.
# placeholder with unique identity
_no_default = object()

def find(predicate, values, default = _no_default):
   ...

Of course a language can provide tools and syntactic sugar to make this simpler.
> **Comments:**
> **username_13** (2024-12-24): In Ruby, default arguments are re-evaluated on each call, unlike Python's which are evaluated only once. So, a very similar pattern in Ruby is something like `def find(predicate, values, default = (no_argument_passed = true; nil))`. This will assign `true` to `no_argument_passed` IFF the default argument was evaluated, i.e., no argument was passed. If an argument is passed, `no_argument_passed` will be defined but not initialized, which means it will evaluate to `nil`, which is falsy. The Rubinius Ruby implementation had an internal, unspeakable `undefined` value for this use case.  
> **username_13** (2024-12-24): In Rubinius, large parts of the core library were written in Ruby, but there are many methods in the core library which have semantics that are hard to express in Ruby. (A vestige of there being no specification and only a single implementation with most of the core library written in C with privileged access to the interpreter internals.) Being able to reliably determine the number of arguments passed is one of those hard to express semantics – effectively, many methods in the Ruby core library are overloaded on arity, which is not easily accessible from Ruby itself.  
---
### Answer by username_16 on 2024-12-24. Score: 4
It's a little different than your

difference is apparently whether or not it was "intentional"

characterization, but R has a somewhat unique implementation with NULL and NA that addresses the same fundamental issue, where NULL represents an undefined value (internally, it points to a singleton global constant and is effectively treated as its own unique type) and NA represents a scalar value that is missing or unknown. A key thing is that NA values in R are type-aware (there's an internal NA constant for each type) and can be used in place of scalar values; they will even propagate through calculations. They can be thought of as a convenient wrapper around sentinel values and have well-defined rules distinct from NULL.
For reference, I believe the pandas library in Python has implemented something similar to R's NA (specifically through pandas.NA), and Julia (a much newer language) has missing as an NA analog. I don't know the details of these, so they might be interesting to look into as well.
Here's an example where I create a vector and try replacing values with NA and NULL:
vec = 1:5
vec
# [1] 1 2 3 4 5

vec[2] = NA
vec
# [1]  1 NA  3  4  5

vec[4] = NULL
# Error in vec[4] = NULL : replacement has length zero
vec
# [1]  1 NA  3  4  5

The NULL assignment failed because I couldn't put something of zero length into a slot that required something of length 1.
If I try to create a vector with each, NA is retained, NULL is not:
c(1, NA, 3, NA, 5)
# [1]  1 NA  3 NA  5

c(1, NULL, 3, NULL, 5)
# [1] 1 3 5

And with math, NA values are preserved:
vec + 3
# [1]  4 NA  6  7  8

They help distinguish between optional unknown inputs and required unknown inputs in defining functions:
f = function(a = NA, b) { # equivalent to b = NULL as a default
  print(a)
  print(b)
}

f(a = 1)
# Error in print(b) : argument "b" is missing, with no default

f(b = 2)
# [1] NA
# [1] 2

Thinking about return values from functions, getting NA as a result indicates that there is a value; you just don't know what it is:
mean(vec)
# [1] NA

Whereas NULL indicates that nothing was returned from a function.
Now, that doesn't mean the design is perfect and all the rules are intuitive, far from it. Keep in mind, though, that R is a statistical language that relies heavily on vector and data table structures in applications where missing data can be common. The types of language design decisions that best improve the daily experience of a statistician using R vs. a web developer using JavaScript are going to be different.
> **Comments:**
> **username_17** (2024-12-25): I like that you mention R but I don't think its rules are consistent. As you show, assigning `NULL` to an element of a vector raises an error but creating a vector with `c(1, NULL)` doesn't. Why should that be? Also `list(a=NULL)` is different to `l=list(a=1);l[[1]]=NULL` or `l=list(a=1);l$a=NULL`, which doesn't make sense to me. Additionally, `TRUE || NULL` is `TRUE` while `FALSE || NULL` raises an error (admittedly because the RHS isn't evaluated in the first case rather than directly related to `NULL` - but it can cause surprising errors). And let's not even get into `NaN`.  
> **username_18** (2024-12-25): The typical NULL usuage would be to make an empty vector or list and then to add elements to it. If you have a NULL vector and concatenate the number 3 to it, afterwards you have a vector of length 1 with the value 3. If instead you start with a vector NA, it will have length 1 and if you concatenate the number 3 to it, you will get a vector of length 2 containing (NA, 3).  
> **username_16** (2024-12-25): @SamR Yeah, by no means do I think all of the R's rules make sense, at least not without more context of the reasoning behind those decisions. Part of the reason I mentioned Julia is because it might be interesting to see what it does differently from R as a newer language with the benefit of hindsight. I just haven't done much with Julia to be able to say.  
> **username_18** (2024-12-25): @SamR A single element of a vector has length 1 so you can't put NULL there. c(..) on the other hand, concatenates its arguments and each argument can already have a length, so you can put numbers in there (length 1), vectors (length >=1) or NULL (length 0).  
> **username_16** (2024-12-25): @quarague I think it's less about how it works and more about the why. It is reasonable to ask why the creators did that instead of treating it as an error. I suspect there are pros and cons to both ways. Likewise, instead of making assigning NULL to an existing vector element an error, they could have made it remove the element instead. Maybe there are good reasons from a user standpoint for all their decisions. But I also wouldn't be surprised if some decisions were based on implementation complexity, performance, or weird interactions with other things.  
> **username_17** (2024-12-25): @quarague I suppose there's a logic to that but it's not the only way to do concatenation. In Python `[1] + [None]` is not the same as `[1] + []` and intuitively that makes more sense to me. In any case it still doesn't seem consistent that `l=list(NULL); l[[1]]=NULL` creates a list of length one and then a list of length zero.  
---
### Answer by username_19 on 2024-12-24. Score: 1
If the language doesn't provide this, programmers who need the distinction will end up implementing it themselves, perhaps using a clumsy structure like:
struct optional_or_null {
  enum {
    UNDEFINED;
    NULL;
    DEFINED;
  } flag;
  T value;
}

Putting it in the language definition means we don't get myriad, incompatible definitions like this. This then paves the way for common programming conventions so we use the built-in null vs. undefined distinction consistently.
Of course, conventions don't force this consistency, but it's still better than everyone rolling their own designs.
We may also be able to do better in language designs. JavaScript allows you to assign undefined explicitly. If undefined is supposed to represent "not initialized", this seems to contradict it -- you can't initialize something to "not initialized". Ideally we should only be able to test whether a variable equals undefined, not pass it around as a first-class value.
On the other hand, second-class values raise their own problems. If you have a function that compares values, it's frustrating if you can't pass undefined as the value to compare with.
> **Comments:**
> **username_11** (2024-12-27): This seems to assume that two undefined values is exactly what all those downstream users will need, which is the very assumption questioned here.  
---
### Answer by username_20 on 2024-12-25. Score: 1
I propose to look at how missing values are dealt with in other fields, specifically, databases.

The SQL standard provides `NULL` value. But for specific use cases, Ralph Kimball [recommends](https://www.kimballgroup.com/data-warehouse-business-intelligence-resources/kimball-techniques/dimensional-modeling-techniques/null-dimension-attribute/) avoiding using it and providing meaningful not-NULL values instead.

Following this recommendation, in my recent project I had 8 different replacements for `NULL` values, each signaling the reason why the data are missing: `all`, `explicit empty`, `not applicable`, `invalid`, `multiple`, `not yet created` etc. Separate values primarily and greatly simplify debugging. But some business logic could also rely on them.

So, there are more criteria to use null than just 'whether or not it was "intentional"'. Two values aren't enough to cover all use cases. And that's why I think having both `null` and `undefined` is a useless complication.

I recommend this:

1. Add just `null` for 'not intentional'. Or forbid uninitialized variables
2. For 'intentional' cases, consider adding a separate type/class/construction, with the ability to provide and get additional info: the reason why it's empty and maybe other

Note that #2 may be not the part of the language core, but provided by a library.
> **Comments:**
> **username_21** (2024-12-26): "Note that #2 may be not the part of the language core, but provided by a library." - unless the library as such is defined to be part of the language core.?  
> **username_20** (2024-12-26): @ghellquist, I don't quite understand your question. By a library, I meant something like optional-js. But it should be maintained by the language creator, not a side developer  
> **username_20** (2024-12-26): And if you imply that a language may not have a concept of library (package, plugin, etc.) at all, IMO, it can't be used for complex programs, and this whole question is not relevant  
> **username_21** (2024-12-26): the point is that how to implement language features may differ. This means that specific library functions may be part of language core definitions and hence not optional.  
---
### Answer by username_22 on 2024-12-26. Score: 0
If you find this feature essential, you should probably add discriminated unions instead, as that covers what you really need to do and more. You want to have a type that is either an X, an unknown X and an inapplicable X (and probably more).

A problem you may encounter is that your users will also need their data store to support this, if it doesn’t then it will be of limited value (for nulls).
---
