Certain expressions can now return undefined (which is distinct from null). This page explains when this happens and why.
Before starting, let’s try to avoid a misunderstanding. undefined is not meant to be actively used by the casual Mix developer, and also not by the builder for modelling data. If another outstanding value is needed that is distinct from the other ones, there is still null, which is simple to use and doesn’t have any special semantics. In the builder, the value undefined corresponds to a non-existing binding and the value null to the programmatic situation of a missing value (e.g. the user hasn’t yet entered anything).
The only point of discussing undefined is that we want to handle some edge cases.
Some expressions return undefined when something unusual happened that could be an error in some situations and is harmless in others. The most common reason why you get undefined is a missing field in an object, e.g.
{ field1: "hello" }.field2
is undefined.
In older versions of Mix we used to return null instead of undefined. However, this turned out to be problematic, because null is a real value that could also be stored inside the object, and it was not directly possible to distinguish between the cases. Also, we could not ensure that null behaves as needed inside queries. For example, look at the query condition:
record.name != "John" && record.city == "New York"
If the fields name or city are missing in the record, this information is not only needed by the directly following operation (i.e. != or ==) but in some situations also by operations that are farther away, i.e. here && or the driver code running the query. The special value undefined bubbles up: if the field access returns undefined, the comparisons are also undefined, and - depending on the second argument - also the result of the conjunction &&.
While undefined bubbles up there needs to be a limit where the soft error is no longer soft, but a real runtime error is indicated that stops the running app. As we don’t want that undefined persists, the simple rule is: it is not allowed to store undefined into any data structure, and when it is nevertheless tried, the app throws the runtime error. As data structures count:
var)Note that you can programmatically catch undefined earlier. If x is a value, the expression x? is true when x is defined. Hence
if (not x?)
fail("got undefined")
else
()
is a check for undefinedness. There is also a shorter version of this:
noDefault(x)
The function noDefault just returns x with the single exception that undefined is turned into a runtime error. (This function is called noDefault because there is also withDefault, see below.)
Many operations now return undefined when the input (or one of the inputs) is undefined. In particular:
+, -, *, /, %, floor, ceil)==, !=, <, <=, >=, >)not - but not and/or, see below)array.length or map.keys