lua-users home
lua-l archive

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


Request for discussion about unified methods in Lua.

At the moment there are two kind of methods in Lua, the tag
methods and the user methods (':' operator).  Here are some
suggestions to make them the same.

First, there are the hard coded tag methods, a mechanism to
modify Lua behavior for some special actions of the virtual
machine.  Most of them are used for operator overloading i.e.
"gettable" and "settable" for the '[]' and '.' operator, "add"
for the '+' operator and so on.

They are managed by tables internal to the virtual machine and
a special API to access these tables.  Creating tag methods
consists of three steps.  First you have to create a 'tag',
a small integer with the newtag function.  This tag is then
attached to individual objects via the settag function.  And
then you may bind C or Lua functions to specific tag/method 
pairs with settagmethod.

Then there is what I call user methods.  It's a syntactical
construct to call functions embedded in an object.  For
example foo:bar(...) calls the method "bar" of object "foo"
and is the same as foo.bar(foo, ...).  There are some limita-
tions though:  this only works directly if the object (foo)
is a table.  Making it to work with other data types requires
strange tag method magic.  And then there's the problem, that
the object data is mixed with method functions.  All of them
is stored in the same table.  And this further requires, that
you have to copy all methods into newly created objects.  Again,
some of this may be circumvented by using tag method magic but
IMHO only in a slow and fragile way.


So here comes my suggestion.  Instead of attaching tags (these
small integers) to objects attach a standard Lua table.  And
further, let the ':' operator access this "method table" of
the object instead of the object itself.


What would that give?  Tag methods are no longer special.
They are just methods that may be called by the virtual
machine. I.e. a foo[bar] may be resolved to foo:gettable(bar)
and a foo+bar to foo:add(bar).  Setting tag methods is just
a foo:method=func.

Then, methods and object data are separate.  So methods may
be attached to user data too.  And, you don't have to copy
methods into each object instance.  Just set the method table.
Because the method table is a simple Lua table, it may contain
"class data", data shared between all objects with the same
method table.

IMHO it not only gives unified methods but even a much more
powerful class mechanism.  And if you really want the old
behavior of user methods you are free to let the method
table be the object itself.


Some more details of the suggested modifications:

 - The function to get and set method tables is called
   'methods'.  Synopsis is: old=methods(obj[,new])
   If called with a second non-nil argument it sets the
   method table of obj to new.  It returns the old method
   table of obj.

 - 'foo:bar' accesses the bar element of the method table of
   foo.  I.e. 'foo:settable = f' assigned a new gettable
   method.  'globals():index = error' is ok, too ;)

 - There are default method tables for each data type.  Tables
   and userdata may have individual method tables.  All other
   objects share the default table of their type.

   I.e. '0:pow = ...' assigns a pow-method for all numbers and
   '"":call = ...' the call method (new name for the function
   tag method, function is a reserved keyword) for strings.
   The 0 and the "" are not special.  Any object of wanted type
   are acceptable.

 - 'foo:bar(...)' calls the function bar from the method table
   of foo with an additional self-arg as the first parameter
   which is set to foo.  (equiv to methods(foo).bar(foo, ...),
   but see below)

 - Accessing method tables is the same as for all other tables.
   That is, they may invocate get/settable/index of their method
   tables.

There are some points to be discussed though:

 - Allow 'foo::bar' to get bar from the method table of foo's
   method table?

 - I would prefer a special syntax to access the method tables
   instead of the 'methods()' function.  But unfortunately 'foo:'
   does not work.  'x=foo: y=bar:' would give a syntax error.
   Ideas?

   The previous two points are really the same problem.  You want
   ':' as an infix and as a postfix operator but that can't be
   handled by Lua's syntax (missing statement separator).  So it
   has to be handled by special syntactical constructs.

 - The point above about accessing the method tables by settable/
   gettable methods may give problems when a table is it's own
   method table and contains get/settable.  Maybe this has to be
   handled specially.  Something like 'if foo=methods(foo) then
   rawget(gettable) else get(gettable)'.

 - The C-API that deals with tag methods has to be changed.
   For compatibility you could just simulate the current numeric
   tags with references to tables (lua_ref()) that are integers
   too.

 - Well, this is the most difficult one ;)  The gc method!
   At the moment the invocation of the gc methods is sorted
   by tag value.  With method tables this becomes difficult.
   There's no inherent ordering of tables.   Adding a gc-
   priority field could give that ordering.  But's hard to
   implement too.  I've no ideas at the moment.


So, what do you think about this?  You get nice methods.  IMHO
much better suited to implement class systems.  You get rid of
tag method craft.  And I think, Lua could even get smaller with
these changes ;)

Ciao, ET.