lua-users home
lua-l archive

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


According to the book programming with lua, the __le operator was
introduced because the operator <= applied to a NaN (Not a Number)
should always return false.
So we can use a table with metamethods to represent a number, and
compare if "X" is less, less or equal, and so on.

But a NaN is not equal (==) with any other number also, even another
NaN. Even with itself.

That's the way that a NaN behave in lua, plain number:
-------------------------------------------------
$ lua -e "nan=0/0; print(nan, nan<=nan, nan<nan, nan==nan)"
-nan    false   false   false
-------------------------------------------------
And in lua 5.1, any complex type is always equal with itself. So we
can't represent a NaN with a table. It will work with two tables with
a NaN inside each table, but won't work with the same table compared
with itself, because the __eq operator compare the table identity
before calling __eq, and return true if someone compare one table with
itself.

For instance, suppose that we want to create a type that encapsulate a
number (lua number).

One possible implementation is:
-------------------------------------------------
custom_number = {}
do
       local custom_number_mt = {
               _eq=function(a,b) return a.n==b.n end;
               __lt=function(a,b) return a.n<b.n end;
               __le=function(a,b) return a.n<=b.n end;
       }
       function custom_number.create(number)
               local mynumber = {n=number}
               setmetatable(mynumber,custom_number_mt)
               return mynumber
       end
end

test_numbers = { -1/0, -0/0, -2, -1, 0, -0, 1, 2, 0/0, 1/0 }

for k,v in ipairs(test_numbers) do
       local test = custom_number.create(v)
       print(type(v), v, '==', v==v, '~=', v~=v, '<', v<v, '<=', v<=v)
       print(type(test), test.n, '==', test==test, '~=', test~=test, '<',
test<test, '<=',test<=test)
       print()
end
-------------------------------------------------
But since the __eq operator is not used unless we are comparing
different variables, the behavior with a plain lua number type and a
table version of that number is different:
-------------------------------------------------
$ lua -e "dofile('custom_number.lua')"
number  -inf    ==      true    ~=      false   <       false   <=      true
table   -inf    ==      true    ~=      false   <       false   <=      true

number  -nan    ==      false   ~=      true    <       false   <=      false
table   -nan    ==      true    ~=      false   <       false   <=      false

number  -2      ==      true    ~=      false   <       false   <=      true
table   -2      ==      true    ~=      false   <       false   <=      true

number  -1      ==      true    ~=      false   <       false   <=      true
table   -1      ==      true    ~=      false   <       false   <=      true

number  0       ==      true    ~=      false   <       false   <=      true
table   0       ==      true    ~=      false   <       false   <=      true

number  0       ==      true    ~=      false   <       false   <=      true
table   0       ==      true    ~=      false   <       false   <=      true

number  1       ==      true    ~=      false   <       false   <=      true
table   1       ==      true    ~=      false   <       false   <=      true

number  2       ==      true    ~=      false   <       false   <=      true
table   2       ==      true    ~=      false   <       false   <=      true

number  -nan    ==      false   ~=      true    <       false   <=      false
table   -nan    ==      true    ~=      false   <       false   <=      false

number  inf     ==      true    ~=      false   <       false   <=      true
table   inf     ==      true    ~=      false   <       false   <=      true
-------------------------------------------------
A variable number is not equal with itself, but the table version of
the number is.

In the example, imagine that we are making a lot of calculations with
the table version of a number. We can end up with two tables,
representing the same number, one behave in the expected way, but the
other no.

I guess that this can be a problem with other complex types too.
I understand that the identity comparison is making the whole
operation more optimized in the common case, but I'm not sure that
assume everything always compare itself to true is the right
thing to do.