[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: new "empty" value/type in Lua?
- From: Philipp Janda <siffiejoe@...>
- Date: Fri, 28 Jun 2013 14:18:26 +0200
Am 28.06.2013 13:03 schröbte Tim Hill:
On Jun 28, 2013, at 2:42 AM, Philipp Janda <siffiejoe@gmx.net> wrote:
There already is a level-3 solution to your problem: Have a look at `table.pack` which supports arrays containing nils.
Table.pack() creates a Lua table that (as the docs state) is *not* an array if the list contains nil values.
Then my suggestion is: Don't use arrays for your SQL result rows! Use
tables instead. `table.pack()` shows how you can replace an array with a
table and still support array-like operations.
In your case this probably is unnecessary anyway, because the user usually knows how many values to expect from a database operation -- he/she has written the SQL statement after all …
No, this is not true. This is a generic library that must pack SQL results .. it has no inherent knowledge of the schema or intent of the user (and nor should it). In such a library it is FAR better than it does NOT make assumptions about the statement or schema.
Then there are other implications to consider:
Should `local x` contain nil or empty? Should `table.pack( nil, nil )` translate the nils into empties or not? What about `{ ... }`? Should `table.unpack( t )` translate empties into nils? Should accessing an empty field invoke __(new)index metamethods? `t[ 1 ] or 0` won't work anymore. You would need two conditions to check for valid table elements. There will be sparse arrays where you *don't* want to use empty anyway, e.g.: `{ n=1000, [499]=1, [500]=2, [501]=1 }` ...
I don't see any of these as very hard to answer:
-- "local x" should initialize to nil as it currently does; I see no reason to change the basic language semantics.
-- table.pack(<anything>) should behave as expected, so table.pack(nil, nil) yields {nil, nil} while table.pack(empty, empty) yields {empty, empty}
-- Same for {…}
-- table.unpack() should not translate nil to empty
In that case you just add another new table type:
1) general table (possibly with holes)
2) proper sequences (no holes, integer keys 1...n)
and additionally
3) proper sequences with empty values.
The array-like tables created by `table.pack` and `{...}` would still be
there, so you don't reduce complexity, you just add ...
"empty" has no magic (unlike nil), for example to my mind "empty"
when used in a boolean context should return true like any other value except
false/nil. "empty" is just a value that is totally ordinary EXCEPT it's
NOT equal to any other Lua value except itself.
We already have that: {}.
And the bonus is: You can have as many different "empty" values as you
want (one for NULL, one for NaN, etc.).
So of course it doesn't
modify __newindex() in any special way. In fact, what you have
highlighted is just how special "nil" is in Lua, and how it is
overloaded in odd ways. For example: "x = nil" has different behaviors
for globals and locals. Yes, I completely understand why, but the fact
is it's different.
And again I'll point out that Lua arrays DO behave oddly when faced with nil. I have no problems with this, I've been developing for so long in so many languages I'm used to god knows how many quirks, but quirks they remain. Stop and think about it:
a = {1,2,3} -- it's an array
a[4] = "hello" -- still an array
a[2] = 1000 -- STILL an array
a[4] = nil -- STILL an array
a[2] = nil -- oops .. not an array any more
a[5] = 99 -- not an array
a[2] = 1 -- Hey! .. I'm an array again
For a junior developer, it's even harder to see when hidden behind locals:
local x, y = 10
a = {1,2,3} -- It's an array
a[2] = y -- Not an array any more
a[2] = x -- Wait .. I'm an array again
Why do you care if it's an "array"? No matter what, you can access the
values for the integer keys from 1 to n. If nil is not allowed as a
value, Lua gives you a fast way to calculate that n. If nil may be in
there somewhere, somebody better tell you where this table is supposed
to end because that information is not obvious!
And if a nil ends up in a table that cannot handle it, that's a bug, and
you have more important things to worry about than the array-status of
the table.
And again, introducing "empty" would only make the situation more
complicated ...
Imagine a Java/C++/C# collection behaving like that. I'd be scratching my head a bit at such a thing. (OK, so Java/C++/C# collections have a LOT of other problems, but two wrongs don't make a right! <grin>).
There are plenty of cases where being able to "mark" an array entry
as done/dead/empty etc is useful as part of an algorithm. In many cases you
can do that using a sentinel value that is outside the normal range of
expected array types and/or values, but it's all very domain specific
What are the cases where you cannot use a sentinel value? `{}` will only
be equal to itself, so this is a natural candidate for a sentinel ...
(can I use an empty string? -1? false? auxiliary state?). I'm basically
arguing that "empty" allows you to do this in a nice, clean,
self-documenting, portable manner. I don't really see anything heretical
in that. To be honest, the most heretical thing is that you would
introduce a new keyword, and THAT can of course break existing code.
--Tim
Philipp