Lua is memory efficient for huge but sparse multi-dimensional arrays,
since nil values aren't stored in the tables.
However, reading and writing to such sparse multi-dimensional arrays can
be quite a hassle. If I want to access the value of
t[a][b][c][d][e], I first have to check whether t[a] is a table, t[a][b]
is a table, t[a][b][c] is a table etc, otherwise Lua throws an error
"Attempt to index a nil value".
For example, to read the value at t[a][b][c][d][e], I cannot simply use:
v = t[a][b][c][d][e]
Instead, the code must look something like:
v = t and t[a] and t[a][b] and t[a][b][c] and t[a][b][c][d] and
t[a][b][c][d][e]
Or, to write a value to t[a][b][c][d][e], I cannot simply use:
t[a][b][c][d][e] = v
Instead, the code must look something like:
if not t then t = {[a] = {[b] = {[c] = {[d] = {[e] = v}}}}}
elseif not t[a] then t[a] = {[b] = {[c] = {[d] = {[e] = v}}}}
elseif not t[a][b] then t[a][b] = {[c] = {[d] = {[e] = v}}}
elseif not t[a][b][c] then t[a][b][c] = {[d] = {[e] = v}}
elseif not t[a][b][c][d] then t[a][b][c][d] = {[e] = v}
else t[a][b][c][d][e] = v
end
I suggest that it would be more useful -- and more consistent -- if the
"Attempt to index a nil value" error is deprecated and, instead, the
indexing of undefined variables does the following:
When reading: simply return nil. Given that undefined variables and
non-existent table entries both return nil, I think it would be more
consistent if an attempt to index an undefined variable also simply
returns nil. (The Lua FAQ states that: "In many languages, trying to
access a non-existent key in a list or dictionary causes an exception;
in Lua the result is simply nil. This is unambiguous because nil cannot
be usefully put into tables.") If t is nil, then t[a] should also
simply be nil, as should t[a][b][c][d][e].
When writing: automatically assign a single-entry table to each
undefined variable, with the given index the sole entry. For example,
if t is an undefined variable, then t[a] = v would be syntactic sugar
for t = {[a] = v}. Similarly, if t[a][b] is already declared as a table
but t[a][b][c] is nil, then t[a][b][c][d][e] = v would mean t[a][b][c] =
{[d] = {[e] = v}}.
-- Anton
This comes up very, very frequently.
This is absolutely a non-starter for inclusion in stock Lua, but for your own programs there are a number of solutions that have been proposed and/or implemented.
The very simplest thing you can do is to implement something like Lodash's get and set methods:
function get(tbl, k, ...)
if tbl == nil or tbl[k] == nil then return nil end
if select('#', ...) == 0 then return tbl[k] end
return get(tbl[k], ...)
end
function set(tbl, k, maybeValue, ...)
if select('#', ...) == 0 then
-- this will throw if the top-level tbl is nil, which is the desired behavior
tbl[k] = maybeValue
return
end
if tbl[k] == nil then tbl[k] = {} end
set(tbl[k], maybeValue, ...)
end
Then t[a][b][c][d] can be expressed as `get(t, a, b, c, d)` and t[a][b][c][d] = e can be expressed as `set(t, a, b, c, d, e)`.
If you want the syntax you've given, then this will work for read-only:
debug.setmetatable(nil, { __index = function(t, k) return nil end })
After this, `(nil).a.b.c` will simply return nil. This may break existing code, so be judicious where you use it.
Automatically creating nested tables is quite a bit more controversial and I really do recommend the set() function above for that. I'm not sure if you can implement this in vanilla Lua without a patch.
/s/ Adam