Suggestion: Deprecate "Attempt to index a nil value" error; instead, return nil or create table

classic Classic list List threaded Threaded
35 messages Options
12
Reply | Threaded
Open this post in threaded view
|

Suggestion: Deprecate "Attempt to index a nil value" error; instead, return nil or create table

Anton Jordaan
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


Reply | Threaded
Open this post in threaded view
|

Re: Suggestion: Deprecate "Attempt to index a nil value" error; instead, return nil or create table

Coda Highland
On Fri, Feb 28, 2020 at 3:59 PM Anton Jordaan <[hidden email]> wrote:
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.

If you really do want to modify Lua, http://lua-users.org/wiki/LuaPowerPatches has a "safe table navigation" patch for 5.2 that may give you some insight.

/s/ Adam
Reply | Threaded
Open this post in threaded view
|

Re: Suggestion: Deprecate "Attempt to index a nil value" error; instead, return nil or create table

Egor Skriptunoff-2
In reply to this post by Anton Jordaan
On Sat, Feb 29, 2020 at 12:59 AM Anton Jordaan wrote:
If I want to access the value of t[a][b][c][d][e]

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.
See solution #3


When writing: automatically assign a single-entry table to each
undefined variable, with the given index the sole entry.

Creating a table would better be explicit.
Reply | Threaded
Open this post in threaded view
|

Re: Suggestion: Deprecate "Attempt to index a nil value" error; instead, return nil or create table

Soni "They/Them" L.


On 2020-02-28 10:05 p.m., Egor Skriptunoff wrote:

> On Sat, Feb 29, 2020 at 12:59 AM Anton Jordaan wrote:
>
>     If I want to access the value of t[a][b][c][d][e]
>
>     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.
>
>
>
> http://lua-users.org/lists/lua-l/2017-02/msg00308.html
> See solution #3
>
>
>     When writing: automatically assign a single-entry table to each
>     undefined variable, with the given index the sole entry.
>
>
> Creating a table would better be explicit.

mktable -p (table, a, b, c, d, e, f, g, ...).with = value

(this is valid lua fwiw, but good luck making it work :v)

Reply | Threaded
Open this post in threaded view
|

Re: Suggestion: Deprecate "Attempt to index a nil value" error; instead, return nil or create table

Soni "They/Them" L.


On 2020-02-28 10:10 p.m., Soni "They/Them" L. wrote:

>
>
> On 2020-02-28 10:05 p.m., Egor Skriptunoff wrote:
>> On Sat, Feb 29, 2020 at 12:59 AM Anton Jordaan wrote:
>>
>>     If I want to access the value of t[a][b][c][d][e]
>>
>>     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.
>>
>>
>>
>> http://lua-users.org/lists/lua-l/2017-02/msg00308.html
>> See solution #3
>>
>>
>>     When writing: automatically assign a single-entry table to each
>>     undefined variable, with the given index the sole entry.
>>
>>
>> Creating a table would better be explicit.
>
> mktable -p (table, a, b, c, d, e, f, g, ...).with = value
>
> (this is valid lua fwiw, but good luck making it work :v)

correction, this is valid lua:

(mktable -p (table, a, b, c, d, e, f, g, ...)).with = value

oh well .-.

Reply | Threaded
Open this post in threaded view
|

Re: Suggestion: Deprecate "Attempt to index a nil value" error; instead, return nil or create table

Italo Maia
Perl has auto-vivification and it creates way more problems than it solves. Also agree this should not be part of the language and the error when trying to index a null value is a good thing.

On Sat, Feb 29, 2020, 02:12 Soni "They/Them" L. <[hidden email]> wrote:


On 2020-02-28 10:10 p.m., Soni "They/Them" L. wrote:
>
>
> On 2020-02-28 10:05 p.m., Egor Skriptunoff wrote:
>> On Sat, Feb 29, 2020 at 12:59 AM Anton Jordaan wrote:
>>
>>     If I want to access the value of t[a][b][c][d][e]
>>
>>     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.
>>
>>
>>
>> http://lua-users.org/lists/lua-l/2017-02/msg00308.html
>> See solution #3
>>
>>
>>     When writing: automatically assign a single-entry table to each
>>     undefined variable, with the given index the sole entry.
>>
>>
>> Creating a table would better be explicit.
>
> mktable -p (table, a, b, c, d, e, f, g, ...).with = value
>
> (this is valid lua fwiw, but good luck making it work :v)

correction, this is valid lua:

(mktable -p (table, a, b, c, d, e, f, g, ...)).with = value

oh well .-.

Reply | Threaded
Open this post in threaded view
|

Re: Suggestion: Deprecate "Attempt to index a nil value" error; instead, return nil or create table

Egor Skriptunoff-2
On Sat, Feb 29, 2020 at 5:42 PM Italo Maia wrote:
the error when trying to index a null value is a good thing.



Let's assume the statement
v = t[a][b][c][d][e]
Each of 5 indexing steps might result in nil.
Sometimes you want to raise "Attempt to index a nil value" exception,
sometimes you want to ignore the exception.

If nil is indexable, then it's pretty easy to raise the exception
v = assert(t[a][b][c][d])[e]

But if nil is non-indexable, it's not easy to ignore the exception
v = (((((t or {})[a] or {})[b] or {})[c] or {})[d] or {})[e]

Indexable nil looks like a better alternative.

Would indexable nil reduce runtime error detection?   No.
Almost always another exception will be raised soon:
"Attempt to perform arithmetic on a nil value" and the like.

Reply | Threaded
Open this post in threaded view
|

Re: Suggestion: Deprecate "Attempt to index a nil value" error; instead, return nil or create table

Soni "They/Them" L.


On 2020-02-29 1:31 p.m., Egor Skriptunoff wrote:

> On Sat, Feb 29, 2020 at 5:42 PM Italo Maia wrote:
>
>     the error when trying to index a null value is a good thing.
>
>
>
> Let's assume the statement
> v = t[a][b][c][d][e]
> Each of 5 indexing steps might result in nil.
> Sometimes you want to raise "Attempt to index a nil value" exception,
> sometimes you want to ignore the exception.
>
> If nil is indexable, then it's pretty easy to raise the exception
> v = assert(t[a][b][c][d])[e]
>
> But if nil is non-indexable, it's not easy to ignore the exception
> v = (((((t or {})[a] or {})[b] or {})[c] or {})[d] or {})[e]
>
> Indexable nil looks like a better alternative.
>
> Would indexable nil reduce runtime error detection?   No.
> Almost always another exception will be raised soon:
> "Attempt to perform arithmetic on a nil value" and the like.
>

v = (pcall(function() t[a][b][c][d][e] end) or nil) and t[a][b][c][d][e]

slightly cleaner.

Reply | Threaded
Open this post in threaded view
|

Re: Suggestion: Deprecate "Attempt to index a nil value" error; instead, return nil or create table

Philippe Verdy-2
I see no value at all in adding "or nil" (except that you'll change a
"false" stored value, returned without exception, into an "nil"). Then
why do you need "and t[a][b][c][d][e]" ? Wouldn' this be enough?:

 _, v = pcall(function() return t[a][b][c][d][e] end)

i.e. discard (_) the false/true status in the 1st return value of the
pcall, use the second value which is explicitly returned and will be
nil if the function does not return but throws an exception.

Le sam. 29 févr. 2020 à 17:43, Soni "They/Them" L. <[hidden email]> a écrit :

>
>
>
> On 2020-02-29 1:31 p.m., Egor Skriptunoff wrote:
> > On Sat, Feb 29, 2020 at 5:42 PM Italo Maia wrote:
> >
> >     the error when trying to index a null value is a good thing.
> >
> >
> >
> > Let's assume the statement
> > v = t[a][b][c][d][e]
> > Each of 5 indexing steps might result in nil.
> > Sometimes you want to raise "Attempt to index a nil value" exception,
> > sometimes you want to ignore the exception.
> >
> > If nil is indexable, then it's pretty easy to raise the exception
> > v = assert(t[a][b][c][d])[e]
> >
> > But if nil is non-indexable, it's not easy to ignore the exception
> > v = (((((t or {})[a] or {})[b] or {})[c] or {})[d] or {})[e]
> >
> > Indexable nil looks like a better alternative.
> >
> > Would indexable nil reduce runtime error detection?   No.
> > Almost always another exception will be raised soon:
> > "Attempt to perform arithmetic on a nil value" and the like.
> >
>
> v = (pcall(function() t[a][b][c][d][e] end) or nil) and t[a][b][c][d][e]
>
> slightly cleaner.
>

Reply | Threaded
Open this post in threaded view
|

Re: Suggestion: Deprecate "Attempt to index a nil value" error; instead, return nil or create table

Soni "They/Them" L.


On 2020-02-29 1:56 p.m., Philippe Verdy wrote:
> I see no value at all in adding "or nil" (except that you'll change a
> "false" stored value, returned without exception, into an "nil"). Then
> why do you need "and t[a][b][c][d][e]" ? Wouldn' this be enough?:
>
>   _, v = pcall(function() return t[a][b][c][d][e] end)
>
> i.e. discard (_) the false/true status in the 1st return value of the
> pcall, use the second value which is explicitly returned and will be
> nil if the function does not return but throws an exception.

actually it'll be the string "attempt to index a nil value"

>
> Le sam. 29 févr. 2020 à 17:43, Soni "They/Them" L. <[hidden email]> a écrit :
> >
> >
> >
> > On 2020-02-29 1:31 p.m., Egor Skriptunoff wrote:
> > > On Sat, Feb 29, 2020 at 5:42 PM Italo Maia wrote:
> > >
> > >     the error when trying to index a null value is a good thing.
> > >
> > >
> > >
> > > Let's assume the statement
> > > v = t[a][b][c][d][e]
> > > Each of 5 indexing steps might result in nil.
> > > Sometimes you want to raise "Attempt to index a nil value" exception,
> > > sometimes you want to ignore the exception.
> > >
> > > If nil is indexable, then it's pretty easy to raise the exception
> > > v = assert(t[a][b][c][d])[e]
> > >
> > > But if nil is non-indexable, it's not easy to ignore the exception
> > > v = (((((t or {})[a] or {})[b] or {})[c] or {})[d] or {})[e]
> > >
> > > Indexable nil looks like a better alternative.
> > >
> > > Would indexable nil reduce runtime error detection?   No.
> > > Almost always another exception will be raised soon:
> > > "Attempt to perform arithmetic on a nil value" and the like.
> > >
> >
> > v = (pcall(function() t[a][b][c][d][e] end) or nil) and t[a][b][c][d][e]
> >
> > slightly cleaner.
> >
>


Reply | Threaded
Open this post in threaded view
|

Re: Suggestion: Deprecate "Attempt to index a nil value" error; instead, return nil or create table

Philippe Verdy-2
Hmmm.. yes, but still "or nil" is not needed.

For such situation, where you expect a function to return a value or
an exception to catch in order to use an alternate (nil) value, I'd
use a utility function to use pcall():

function onerror(func, alt)
  local ok, value = pcall(func)
  if ok then return value end
  return alt
end

then:

v = onerror(function() return t[a][b][c][d][e] end, nil)

where you can choose which alternate value to use (not necessarily
nil, could be 0 or an empty string or anything else)

Le sam. 29 févr. 2020 à 19:07, Soni "They/Them" L. <[hidden email]> a écrit :

>
>
>
> On 2020-02-29 1:56 p.m., Philippe Verdy wrote:
> > I see no value at all in adding "or nil" (except that you'll change a
> > "false" stored value, returned without exception, into an "nil"). Then
> > why do you need "and t[a][b][c][d][e]" ? Wouldn' this be enough?:
> >
> >   _, v = pcall(function() return t[a][b][c][d][e] end)
> >
> > i.e. discard (_) the false/true status in the 1st return value of the
> > pcall, use the second value which is explicitly returned and will be
> > nil if the function does not return but throws an exception.
>
> actually it'll be the string "attempt to index a nil value"
>
> >
> > Le sam. 29 févr. 2020 à 17:43, Soni "They/Them" L. <[hidden email]> a écrit :
> > >
> > >
> > >
> > > On 2020-02-29 1:31 p.m., Egor Skriptunoff wrote:
> > > > On Sat, Feb 29, 2020 at 5:42 PM Italo Maia wrote:
> > > >
> > > >     the error when trying to index a null value is a good thing.
> > > >
> > > >
> > > >
> > > > Let's assume the statement
> > > > v = t[a][b][c][d][e]
> > > > Each of 5 indexing steps might result in nil.
> > > > Sometimes you want to raise "Attempt to index a nil value" exception,
> > > > sometimes you want to ignore the exception.
> > > >
> > > > If nil is indexable, then it's pretty easy to raise the exception
> > > > v = assert(t[a][b][c][d])[e]
> > > >
> > > > But if nil is non-indexable, it's not easy to ignore the exception
> > > > v = (((((t or {})[a] or {})[b] or {})[c] or {})[d] or {})[e]
> > > >
> > > > Indexable nil looks like a better alternative.
> > > >
> > > > Would indexable nil reduce runtime error detection?   No.
> > > > Almost always another exception will be raised soon:
> > > > "Attempt to perform arithmetic on a nil value" and the like.
> > > >
> > >
> > > v = (pcall(function() t[a][b][c][d][e] end) or nil) and t[a][b][c][d][e]
> > >
> > > slightly cleaner.
> > >
> >
>
>

Reply | Threaded
Open this post in threaded view
|

Re: Suggestion: Deprecate "Attempt to index a nil value" error; instead, return nil or create table

connor horman
In reply to this post by Soni "They/Them" L.
I’ve used some strongly typed languages that have a “try operator”. I think lua could use something similar.
In this case something like t?[a]?[b]?[c]?[d]?[e], would be equivalent to t[a][b][c][d][e], but if any evaluation preceded by a ? has a false value (false or nil), the result of the entire expression is that value (short circuiting). This only short circuits out of chains of index expressions (of all forms), and function calls. Would that be something that would work in lua?

On Sat, Feb 29, 2020 at 13:07 Soni "They/Them" L. <[hidden email]> wrote:


On 2020-02-29 1:56 p.m., Philippe Verdy wrote:
> I see no value at all in adding "or nil" (except that you'll change a
> "false" stored value, returned without exception, into an "nil"). Then
> why do you need "and t[a][b][c][d][e]" ? Wouldn' this be enough?:
>
>   _, v = pcall(function() return t[a][b][c][d][e] end)
>
> i.e. discard (_) the false/true status in the 1st return value of the
> pcall, use the second value which is explicitly returned and will be
> nil if the function does not return but throws an exception.

actually it'll be the string "attempt to index a nil value"

>
> Le sam. 29 févr. 2020 à 17:43, Soni "They/Them" L. <[hidden email]> a écrit :
> >
> >
> >
> > On 2020-02-29 1:31 p.m., Egor Skriptunoff wrote:
> > > On Sat, Feb 29, 2020 at 5:42 PM Italo Maia wrote:
> > >
> > >     the error when trying to index a null value is a good thing.
> > >
> > >
> > >
> > > Let's assume the statement
> > > v = t[a][b][c][d][e]
> > > Each of 5 indexing steps might result in nil.
> > > Sometimes you want to raise "Attempt to index a nil value" exception,
> > > sometimes you want to ignore the exception.
> > >
> > > If nil is indexable, then it's pretty easy to raise the exception
> > > v = assert(t[a][b][c][d])[e]
> > >
> > > But if nil is non-indexable, it's not easy to ignore the exception
> > > v = (((((t or {})[a] or {})[b] or {})[c] or {})[d] or {})[e]
> > >
> > > Indexable nil looks like a better alternative.
> > >
> > > Would indexable nil reduce runtime error detection?   No.
> > > Almost always another exception will be raised soon:
> > > "Attempt to perform arithmetic on a nil value" and the like.
> > >
> >
> > v = (pcall(function() t[a][b][c][d][e] end) or nil) and t[a][b][c][d][e]
> >
> > slightly cleaner.
> >
>


Reply | Threaded
Open this post in threaded view
|

Re: Suggestion: Deprecate "Attempt to index a nil value" error; instead, return nil or create table

Egor Skriptunoff-2
On Sat, Feb 29, 2020 at 9:26 PM connor horman wrote:
I’ve used some strongly typed languages that have a “try operator”.
t?[a]?[b]?[c]?[d]?[e] would be equivalent to t[a][b][c][d][e], but if any evaluation preceded by a ? has a false value (false or nil), the result of the entire expression is that value (short circuiting). This only short circuits out of chains of index expressions (of all forms), and function calls.


What about adding "try-version" of every Lua operator?
   t?[index]   = indexing: return nothing if t is nil
   t?.field    = indexing: return nothing if t is nil
   t?:method() = invoking a method, return nothing if t is nil
   func?(arg1,arg2,..) = calling a function: return all the arguments if func is nil
   a ?+ b = adding: (a or 0)+(b or 0)
     ?- a = negating: -(a or 0)
   a ?~ b = XORing: (a or 0)~(b or 0)
   a ?& b = ANDing: (a or -1)&(b or -1)
   a ?* b = multiplying: (a or 1)*(b or 1)
   a ?/ b = dividing: (a or 0)/(b or 1)
   a ?% b = taking modulo: (a or 0)%(b or infinity)
   a ?^ b = raising to power: (a or 0)^(b or 1)
   a ?< b = comparing: nil is replaced with the minimal possible value of the datatype
   a ?== b = comparing: nil is replaced with 0 or empty string depending on the datatype
   a ?.. b = concatenating: (a or '')..(b or '')
      ?# a = taking length: return 0 if a is nil

Reply | Threaded
Open this post in threaded view
|

Re: Suggestion: Deprecate "Attempt to index a nil value" error; instead, return nil or create table

connor horman


On Sat, Feb 29, 2020 at 14:29 Egor Skriptunoff <[hidden email]> wrote:
On Sat, Feb 29, 2020 at 9:26 PM connor horman wrote:
I’ve used some strongly typed languages that have a “try operator”.
t?[a]?[b]?[c]?[d]?[e] would be equivalent to t[a][b][c][d][e], but if any evaluation preceded by a ? has a false value (false or nil), the result of the entire expression is that value (short circuiting). This only short circuits out of chains of index expressions (of all forms), and function calls.


What about adding "try-version" of every Lua operator?
   t?[index]   = indexing: return nothing if t is nil
   t?.field    = indexing: return nothing if t is nil
   t?:method() = invoking a method, return nothing if t is nil
   func?(arg1,arg2,..) = calling a function: return all the arguments if func is nil
   a ?+ b = adding: (a or 0)+(b or 0)
     ?- a = negating: -(a or 0)
   a ?~ b = XORing: (a or 0)~(b or 0)
   a ?& b = ANDing: (a or -1)&(b or -1)
   a ?* b = multiplying: (a or 1)*(b or 1)
   a ?/ b = dividing: (a or 0)/(b or 1)
   a ?% b = taking modulo: (a or 0)%(b or infinity)
   a ?^ b = raising to power: (a or 0)^(b or 1)
   a ?< b = comparing: nil is replaced with the minimal possible value of the datatype
   a ?== b = comparing: nil is replaced with 0 or empty string depending on the datatype
   a ?.. b = concatenating: (a or '')..(b or '')
      ?# a = taking length: return 0 if a is nil
Many of these, excluding ?# don’t have sensible defaults with operator overloading (which we get from metatables). All forms of indexing and function calls are easy: evaluate the expression if the root (t in this case) is neither false nor nil, otherwise, return the root.  Adding the rest would complicate things greatly.

Reply | Threaded
Open this post in threaded view
|

Re: Suggestion: Deprecate "Attempt to index a nil value" error; instead, return nil or create table

Sven Olsen
In reply to this post by connor horman
In this case something like t?[a]?[b]?[c]?[d]?[e], would be equivalent to t[a][b][c][d][e], but if any evaluation preceded by a ? has a false value (false or nil), the result of the entire expression is that value (short circuiting). This only short circuits out of chains of index expressions (of all forms), and function calls. Would that be something that would work in lua?

Back in 2013, I added both "safe navigation" and "required field" shorthands to my Lua parser.  So t?[a]?.b will evaluate as nil if any of the component expressions are nil, while t![a]!.b will instead throw an error message like "<expression> is missing required field 'b'", if, say, t[a].b==nil.  Safe navigation, in various forms, is pretty easy to add to Lua.  A "required field" semantic is harder to mod in, and it's really just a slightly terser way of writing boilerplate asserts, but I think it's still useful to have around.

Since I wrote that patch, Apple's Swift has become quite popular, and while it's a strongly typed compiled language, Swift's syntax for optional and required fields is fairly similar to what I did inside my Lua parser.  Thanks to Swift, a lot more programmers have become used to '?' and '!' semantics.  That said, the details of how, exactly, the semantics of either ought to work in the context of Lua remain highly debatable (and those details have been debated on this list in the past).

-Sven
Reply | Threaded
Open this post in threaded view
|

Re: Suggestion: Deprecate "Attempt to index a nil value" error; instead, return nil or create table

Egor Skriptunoff-2
In reply to this post by connor horman
On Sat, Feb 29, 2020 at 10:35 PM connor horman wrote:
Many of these, excluding ?# don’t have sensible defaults with operator overloading (which we get from metatables).

Overloaded operators should receive unmodified nil.
If obj has addition overloaded in its metatable then
obj ?+ nil
should be the same as
obj + nil

Reply | Threaded
Open this post in threaded view
|

Re: Suggestion: Deprecate "Attempt to index a nil value" error; instead, return nil or create table

Dennis Fischer
In reply to this post by Anton Jordaan
Wouldn't it be enough to write a function to do this type-checking for
you instead of having the language *always* do it?

‣ Doing this would make debugging harder, as error messages wouldn't
tell you what's missing, but just that somewhere in the chain of values
there's a nil
‣ I assume the additional condition might have a negative impact on
performance, though this might be very small and irrelevant
‣ Ruby does this with the safe navigation operator `nil&.do_stuff`;
maybe something similar would be a better choice (though I personally
don't think it's necessray)

You could easily use something like this:

local function index(object, idx, ...)
    if idx then
        if object[idx] then
            return index(object, ...)
        end
    else
        return object
    end
end
local value = index(some_4d_table, x, 20, z, "foobar")

To traverse a list of nested tables until the end of a list of keys or a
missing key in some subtable (which would then return nil). Similarly,
you could write a function like this to insert missing tables according
to a chain of indices:

local function insert(object, value, idx, ...)
    if ... then
        if not object[idx] then
            object[idx] = {}
        end
        insert(object, value, ...)
    else
        object[idx]  = value
    end
end
insert(some_4d_table, "Hello, World!", 'foo', 'bar', 'baz', 'message')

On 28/02/2020 22:59, Anton Jordaan wrote:

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


signature.asc (849 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Suggestion: Deprecate "Attempt to index a nil value" error; instead, return nil or create table

Sean Conner
In reply to this post by Anton Jordaan
It was thus said that the Great Anton Jordaan once stated:
>
> 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}}.

  Let's assume for a second this exists.  Here are some of the issues that
need to be addressed for this to work.

1) t[a] = v

        This is syntactic sugar for t = { [a] = v }.  Then does that mean:

                local t[a] = v

        is legal?  Otherwise, this may lead to a proliferation of unintended
        globals.  Also, what about:

                t[a],t[b],t[c] = 1,2,3

        How does this play out, syntactic surgar wise?  Because as it is,

                local t[a],t[b],t[c] = 1,2,3

        is similar to:

                local a,a = 1,2 print(a)

        and it's the second declaration that survives (Lua 5.3).  Weirder,
        in the following:

                a,a = 1,2 print(a)

        it's the first declaration that survives.  This may require more of
        a change to Lua than expected.

2) t[a][b][c][d][e] = v -- when t[a][b] exists

        An asusmption being made is that a,b,c,d,e are all tables.  They
        don't have to be.  It could be a userdata that responds to both
        __index and __newindex to act like a table.  Then this is fine, but
        what if b *doesn't* respond to __newindex?  You either throw an
        error, which is unexpected in this construct or replace b with a
        table, which can cause extended debugging issues trying to figure
        out why the code is failing (most likely in an area far from where
        the bug actually exists!).

3) v = t[a][b][c][d][e]

        v ends up nil.  Which index is nil?

4) t[a] [ t[B] ] [c][d][e] = v

        If t doesn't exist, then what do we end up with?  If B doesn't
        exist, what do we end up with?  It's hard to think about.  The
        assumption is that a,b,c,d,e are all either strings or numbers, but
        in reality, the index can be any value but nil or NaN.  If tables
        are created for missing values, then you end up with a unique table
        as an index in a.

5) t[a][0/0][c][d][e] = v

        The previous question brings this up---what if the index is NaN?
        Rare, but possible.  Treat it as nil?  Error?

  I know a reaction is "don't do that!" or "it's for the case when they are
all tables" but I contend that typos happen, bugs exist, people don't read
the manual closely enough and these issues *will* come up, and need to be
addressed (even if to detect and throw an error).

  -spc

Reply | Threaded
Open this post in threaded view
|

Re: Suggestion: Deprecate "Attempt to index a nil value" error; instead, return nil or create table

Egor Skriptunoff-2
On Mon, Mar 2, 2020 at 2:22 AM Sean Conner wrote:
                local a,a = 1,2 print(a)
        and it's the second declaration that survives (Lua 5.3).  Weirder,
        in the following:
                a,a = 1,2 print(a)
        it's the first declaration that survives. 

The order in a multiple assignment in Lua is UB,
so your program's behavior must not depend on the order.

 
3) v = t[a][b][c][d][e]
        v ends up nil.  Which index is nil?

The whole point of the proposal is to not bother user with such details.
An expression should "just work".

 
  I contend that typos happen, bugs exist 

Yes, the expression t[a][b][c][d][e] will not detect a mistake.
But returned nil would probably raise an error on the next line of your code
(if the logic of your program expects non-nil value).

The point is that we need "silent" version of t[a][b][c][d][e] much more frequently
than "error-raising" version.
We can easily convert "silent" version to "error-raising" by applying assign() to the result.
But the reverse conversion is significantly harder.


Reply | Threaded
Open this post in threaded view
|

Re: Suggestion: Deprecate "Attempt to index a nil value" error; instead, return nil or create table

Roberto Ierusalimschy
In reply to this post by Anton Jordaan
> 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}}.

Note that Lua does not index variables, it indexes values. For instance,
in the expression 'f(x).k = 1', if 'f(x)' is nil, there is no variable
to get the new table.

-- Roberto

12