lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Another nil-in-table proposal here. I wanted to post this as
observation to the Dirk proposal, but eventually I decided to start a
new thread to avoid confusion. Just to catch your interest before a
long post, this is the core of my proposal:

- Two tables mode: Keep-mode never deletes content; Normal-mode
deletes nil content every time it is accessed, also if it is only for
read.

Now, the explanation...

The main problems I want to address, are:

On Wed, Mar 14, 2018 at 7:24 PM, Roberto Ierusalimschy
<roberto@inf.puc-rio.br> wrote:
> The problems I am trying to solve are these:
>
> 1) A constructor like {x, y, z} should always create a sequence with
> three elements. A constructor like {...} should always get all arguments
> passed to a function. A constructor like {f(x)} should always get
> all results returned by the function. '#' should always work on these
> tables correctly.
>
> 2) A statement like 't[#t + 1] = x' should always add one more element
> at the end of a sequence.
>
> These are what confuse people all the time, these are what start new
> rounds of discussions around '#'.

It seems that to this purpose we need somehow to store nil in table as
any other value. However, what really tables did with nil is a
next-order problem, the main concern is how users interact with table
content. For the simple access THERE IS NO DIFFERENCE between nil in
table or not:

```
local t = {}
t[a] = 1
t[a] = nil
assert(t[a] == nil)
```

Does it means that nil is in the table? Does it means that the the
value is missing? Who cares? Chose the best semantic for your problem
and go ahead.

But, there is actually a place where you can distinguish the two
cases: table iteration (# is a form of table iteration). Pratically, I
think the question is: what the following code should print?

```
for k,v in pairs(t) do
  print(v)
end
```

The first idea could be: let it acts according to a metatable flag. If
the table is in a "Normal mode" it will not print anything. If it is
in a "Keep mode" it will print nil.

However, internally, when the content should be actually deleted ?
Well, if a normal-mode table is asked to access a nil value (also just
for read), it should be coherent to remove it before anything else.
This also autmatically gives us the two kind of iteration.

Note that it is just a direct extension of what already happen: now
lua remove stuff when a nil is assigned, I am proposing to remove also
when nil is read.

Summing up, the porposal is:
- Table can act in two mode "Normal mode" and "Keep mode" according to
a flag in its metatable
- Keep-mode tables store nil and it never deletes contents
- Normal-mode tables will deletes contents every times a nil is
assigned or read (as its content).

Some details on how table works with this proposal:
- The tables constructors always put nil inside the table, but then
the table is left in normal-mode. [1]
- When iterating over keep-mode tables, nil values are returned as any
other value.
- When iterating over normal-mode tables, if a nil is found it is
deleted just before the iteration code is run, i.e. it never happen to
get a nil value in the iteration code.

Now, back to the initial issues. With this proposal we still need to
add a call like:

```
local function f() return 1,nil,2 end

local a = {f()}
set_keep_mode(a)

assert(#a == 3)

a[#a+1] = nil
assert(#a == 4)
```

Instead of `set_keep_mode` one can use a constructor function like
`local a = keep{f()}`. In any case, it is a bit verbose, but
acceptable for me. [2]

I would like to try to write a patch, but it seems to me that enabling
both table mode is not trivial in the current lua source
(LUA_NILINTABLE actually changes the definition of emptyness
everywhere). So, before to start hacking, I would like to know your
opinion.

Pocomane

[1] This is for compatibility. If it was a completely new language,
probably, I would use the keep-mode as the default.

[2]  At least for the # operator, it sould be improved with: "The
default # operator never deletes stuff". But it sounds a bit too much
as an exception to me. I do not know if it worth.