So you can essentially choose between warts, garbage factory, or space
hog. The current choice of Lua/table.pack is warts.
Let's try:
0) define __something as "reserved" words.
1) Define the table as its own "metatable" by default, namely, search for __something when appropriate and fallback to the one defined by setmetatable() (standard behavior) (why not?)
change the manual from
rawget(getmetatable(o) or {}, "__ev")
to
(type(o) == 'table' and rawget(o,"__ev")) or rawget(getmetatable(o) or {}, "__ev")
2) Define __len to accept integers >= 0 OR a function() OR a table. If integer >=0 then O(1) # "by definition".
We are assuming 0) and 1) and 2) thereafter
3) Define table.pack() as the default "stack mirror", and (automatically)
embed an additional __newindex to update __len when appropriate.
table.unpack(), with a defined # (__len), just is a "stack mirror".
I think that pack() and unpack() are used mainly with "the stack" or known sequences that almost don't change, and consequently do not trigger __newindex, implying almost no overhead.
4) For the rare occasions that you do not want a sequence from table.pack() just do t.__len = nil t.__newindex = nil.
Then,
a) # will be (more) useful since
b) every other table.something() will function as expected with no extra (meta)table required or GC work and __len is much better (and a "reserved" word) than .n .
c) use table.(un)pack as the default to (un)pack (to)from "the stack" and leave { } for the cases that you known it is appropriate.
side-effect: we have a standard (and a bit faster, since we do not need to getmetatable everywhere) "unbounded array" constructor
a = table.pack()
with well defined length and compatible with the stack.