lua-users home
lua-l archive

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


Gavin Kistner wrote:
One of Lua's greatest assets is the simplicity of the core. No Arrays and Hashes and Queues, we have simply Tables.

The merging of all of these concepts into one atomic type, however, has the collision of embedded nils - tables-as-hashes need a way to remove a key entirely; tables-as-arrays need a way to store an 'empty' value.

For the purposes of this discussion, let's assume that it makes sense to have both nil (nothingness, no value) and false (a value indicating non-truth) in the Lua language. We could go one step farther, like JavaScript, and add a third, similar value: 'undefined'. (That's what it's called in JavaScript land; we could call it whatever we want.)

In JavaScript, this:
    function foo( a, b ){
       alert( a + ':' + b )
    }
    foo( null )
reports:
    "null:undefined"

In JavaScript this:
    var myHash = { foo:null }
    alert( myHash.foo + ':' + myHash.bar )
reports:
    "null:undefined"

And to remove a key from a hash, you use the delete operator:
    delete myHash.foo

In JavaScript, null is not equal to false, but it IS equal to undefined. They are two shades of a very similar concept.

This I don't get.  Your demonstrations show there is a difference
between what Java calls 'null' and what it calls 'undefined'.  They are
not the same thing.  What do you mean by saying they are 'equal'?

My proposal is to use a concept similar to JavaScript (3 'dead' values) but in a slightly different way.
    * false is a value that means "I am not true!"
* undefined (or other name) is a 'non-value' that means "This key exists, but has no useful value!" * nil is the current non-value that means 'emptiness', and removes any keys that have it as a value.

Calling a vararg function would transfer nil values into undefined values in the table:
    function foo( ... )
       for i,v in ipairs( {...} ) do print( i,v,v==nil ) end
    end
    foo( nil, 2, nil )
    --> 1    undefined    true
    --> 2    2    false
    --> 3    undefined    true

It looks like you have 'nil' going in, and 'undefined' coming out...
being generous, I'll say there is some implicit casting happening here,
but I don't see where or why. Also, it looks like (undefined == true). As previously, how can you say two things are equal and different?

I'm sure my idea is not without it's flaws, but hopefully there is some merit in it. In my humble, Lua-newb opinion, the embedded-nil topic has been brought up enough to warrant (yet another?) attempt to fix it. I understand the clean design that brought Lua to its current point, but it _seems_ to me that people need a true solution.

I think the solution has to be:
    a) something like the above, allowing embedded nils; or
b) internal tracking of the true end of an array with array-specific iterators for tables; or
    c) a whole new array type of object that sits alongside tables

The 'solution' cannot be "track the internal size yourself and iterate it yourself" specifically because it is impossible in 5.1 to pass nil values to vararg functions and know that the user explicitly passed an empty value.

Late on the bandwagon...

This is actually the most appealing idea on 'shades of false' I have
seen so far.

I used to be horrified, but I thought about it and now see the sense.
In a nutshell, the idea "nil is the representation of logical-false" was
too deeply ingrained in my head.  Though Lua *had* no *direct* boolean
value representations, it still used boolean values.  The guard on an if
statement is a boolean value, however it may be disguised.  Whatever was
in the place of the guard was (conceptually) automatically and silently
cast to a boolean type, just like "2" in "2"+3 is cast to a numeric
type.  Logical operators aside (they *are* different), the real boolean
value had no existence, except as manifested by the behaviour of if,
while, and the like.  As simple as the mapping, nil -> false, other ->
true, is, it is not an equivalence.  Values take on a new meaning when
used as guards, the meaning logical-false or logical-true.  Now those
meanings have direct representations in Lua.

if (a) then ...

If 'a' is boolean logical-false, then the branch is not executed.
If 'a' is boolean logical-true, then the branch is executed.
If 'a' is not a boolean value, it must be cast to a boolean value, or
the language needs to throw an exception or an error or something.
This third way used to be the *only* working way to use an 'if'
statement. That was actually not clean. The clean if-statements are the first two. I like the introduction of the boolean value type.

The purist in me dislikes syntactic sugar.  (Tired of typing
parentheses?  Give me a break!  Be a man!)  I love Lua because it has so
little, but I find I'd like it better if it had even less.  That part of
me thinks that if the 'a' above is not a boolean value, perhaps it
should be cast to logical-false. (Some reasons provided below.) But, with legacy considerations, the previous casting (nil -> false, other (non-boolean!) -> true) has a better chance of winning out in the end.

One aspect of all this that hasn't gotten as much attention as it
deserves is the behaviour of 'not'.  Is 'not' the same as "(value) ~=
true"?  Is it the same as "(value) == false"?  Is it the same as "(cast
value to boolean) ~= true"?  The last one is the only one that makes
sense to me, but I imagine there will be noise on the alternatives.  (It
is equivalent to the first, if all non-boolean values are cast to
logical-false, and is an argument for casting that way.  Casting to
boolean is exactly the expression "(value) == true". Can't be more direct than that.)

One more point: I like representing sets in Lua by using the index to
represent an element, and non-nil to represent membership of that
element to the set.  No non-nil value in particular is (or, used to be)
more suited to this task than any other.  Assigning 'true' for
membership is slightly distasteful, because the opposite is assigning
'false', and a representation of non-membership is not as clean (or
symmetrical, with empty table representing empty set) as the absence of
a representation of membership.  A single value-type which represents
'this table entry is not empty, but I don't want to say anything more
about it', sounds like your 'undefined', but in a different meaning.  I
don't know if the two should, or could, be made one.  And if they are
bundled together as one symbol, I don't know if it should be cast as
boolean true or false.

Hugh O'Byrne.