[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Tag methods: the next generation
- From: RLake@...
- Date: Wed, 9 Jan 2002 19:59:36 -0500
I was taking a look at this code, from lvm.c, which implements table
access. It seems to me that if I built userdatas which attempted to emulate
both "functions" and "tables", and I used one of those as say a "gettable"
tag method for itself, expecting it to redirect to the "call" tag method
and call that, then not only would that not happen, but that luaV_gettable
would go into an endless loop. Maybe I'm missing something.
Obscure though that scenario might be, it does not strike me off-hand as
completely unreasonable: if the "call" tag method exists to allow me to
make userdata look like functions, it should be allowed to work
consistently.
I think I also agree with Edgar's earlier point about callable tables in
this scenario.
I appreciate the idea of rapid inheritance, but I'm worried that this
particular implementation is not exactly the right way to go about it.
The question is, does this construct increase expressiveness? Because if
efficiency is the issue, I would suggest the implementation of the
"Identity" function/operator (which simply returns its arguments) and the
curry operator/function; that would allow the definition of a constant
closure (one that always returns a given value) as a curry of the identity
function. Trapping the execution of the Identity function during calls
would be quite easy; in fact, the same mechanism could allow for fast
execution of a variety of internal operators (gettable, add, etc.) which
are also handy to curry and for which a library function call is a bit over
the top. In effect, this would create a third type of "Function": the
opcode function. (somewhere in between an operator and a function).
In other words:
always = function(x) return curry1(identity, x) end
metatable(A, {index=always(B)})
If opcode functions were implemented, one possible syntax would be the
Dylan syntax: \op indicates the function associated with op, eg. \+ is the
sum operator. (\and is *not* available in Dylan: "and" is a macro, not an
operator. Before you ask.) But that might be too line-noise-like for a
language like Lua.
If the curry operator were to be implemented, a cool syntax for it might be
function( arg, arg, ... )
as in:
plus_four = \+(4, ...)
But that's just one possibly dumb idea. It might be better to use a lexical
symbol which is not currently used, or to just use some word like "curry"
(obscure though that word is: I tend to think of it is a bind operation,
but Dr. Curry needs all the credit he can get.)
----------------------------------------
--1 say that the "index" tm of t is a userdata which implements a function
through
-- its call TM; i.e. the call TM is a legitimate LUA_FUNCTION. For
whatever reason,
-- I've set the gettable TM to the userdata itself rather than setting it
directly
-- to the function. Perhaps I intend to change the definition of "call"
from time
-- to time, and I only want to change it in one place. Maybe that's dumb.
But let's
-- see what happens. I don't know if the code commentary helps, or even if
the
-- so-called problem is worth thinking about. But here goes. 1==first time
through
-- the loop, 2==second time through.
void luaV_gettable (lua_State *L, StkId t, TObject *key, StkId res) {
const TObject *tm;
init:
--1 the first time through, t is some object whose index TM is a userdata
masquerading
as a function
--2 the second time through, t is now the userdata. We haven't called the
call TM.
if (ttype(t) == LUA_TTABLE) { /* `t' is a table? */
Table *et = hvalue(t)->eventtable;
if ((tm = fasttm(L, et, TM_GETTABLE)) == NULL) { /* no gettable TM? */
const TObject *h = luaH_get(hvalue(t), key); /* do a primitive get
*/
/* result is no nil or there is no `index' tag method? */
if (ttype(h) != LUA_TNIL || /* no nil? */
(tm = fasttm(L, et, TM_INDEX)) == NULL) { /* or no index TM? */
setobj(res, h); /* default get */
return;
}
}
--1 the first time through, t is the table and tm is the userdata "index"
TM.
--1 we will fall through
--2 the second time, t is the userdata TM (i.e. t == tm)
/* else will call the tag method */
} else { /* not a table; try a `gettable' tag method */
--1 we didn't get here the first time.
--2 The second time we get here, and ask for the userdata's gettable TM.
if (ttype(tm = luaT_gettmbyobj(L, t, TM_GETTABLE)) == LUA_TNIL) {
luaG_typeerror(L, t, "index");
return; /* to avoid warnings */
}
}
--2 If we come here again, we're lost.
--1 The first time, we came here
if (ttype(tm) == LUA_TFUNCTION)
callTM(L, tm, t, key, NULL, res);
--1 tm is a userdata and we don't check if it has a call TM. Instead we go
to the else
else {
t = (StkId)tm; /* ?? */
--1 and now t is the userdata
goto init; /* return luaV_gettable(L, tm, key, res); */
}
}