lua-users home
lua-l archive

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


On Mon, May 11, 2009 at 3:47 AM, Flemming Madsen
> Yes i did notice, but I think there is a point to List being a true
> superset of table

The relationship between table, tablex and List could be made more clear.

At first glance List appears intended as an ADT with a private
implementation, whereas tablex is a table of functions that can be
called on raw tables.  In practice, a list is merely a table with its
metatable set to List, which itself is a table of functions that
operate on raw tables.  In fact, calling List:new, doesn't necessarily
create a new object but may may return the originally provided table
after altering its metatable.  In similar manner, a table could have
its metatable set instead to tablex.  This begs the question of why
List ~= tablex.  List and tablex are not so different in abstraction
level as they first appear.

A main difference I see between tablex and List is that tablex
operations return a new list (immutable), whereas List operations
operate in-place (mutable).  It seems arbitrary that to perform an
immutable operation on a table, we can simply call the tablex
operation, whereas to perform a mutable operation, we need to wrap the
table in a List (or at least modify the metatable).  In contrast, the
table functions (e.g. table.sort) are mutable operations, but the name
"tablex" suggests it is an extention to "table" and behaves in similar
manner.

Another difference is that the List function table only includes
operations on the array part of a table (inspired by thinking in terms
of a list ADT or the Python data type), wheres tablex includes some
functions that operate on the array part and some functions that also
operate on the hash part.  This in effect precludes mutable hash part
operations.

The recent change to pass the table as the second argument to tablex
functions goes against the design of the table standard library.  For
example, table.sort(t, func) exists rather than table.sort(func, t).
The former is needed in order to allow table to be set as a metatable
for tables.  The same applies to tablex.  That said, it may be useful
to have a function map(func, t), but not part of the table/tablex
libraries, which would have map(t, func).

I had mentioned the List library to Fabien, who felt it lists were
unluaish, conflicting with the "tables are everything" mindset.  I
agree to some extent.  I would want to use List mainly for the low
level algorithms of the code (where I usually use tables) rather than
for the public API.  Lacking any other provision, I would be tempted
to use the hash part of a List--after all, existing List operations
operate in-place and ignore the hash part.

So, I suggest two libraries: tablex (in-place, mutable operations) and
tablex_immutable (returns new list, immutable operations).  (Note: I
did not name this tablex_mutable since tables are by default mutable,
like standard table functions.)  Creating a so-called "List" would
entail basically setmetable({}, tablex), perhaps a.k.a. tablex{}.  A
decision would need to be made as to whether tablex_immutable
functions set the metatable on each list they return (which was not a
concern with mutable operations).

tablex_immutable is perhaps unnecessary.
tablex_immutable(t):filter(f):map(g) can instead be written more
generally and more efficiently, and maybe more clearly, as
tablex(t):copy():filter(f):map(g).
If you like, a List ADT could use tablex and internally add the copy.
One could also create a table "listx" with the array-part operations
of tablex_immutable copied into it.  There's some flexibility here to
mix-and-match.