[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: setfenv on userdata
- From: Rici Lake <lua@...>
- Date: Wed, 24 Aug 2005 11:07:27 -0500
On 24-Aug-05, at 8:25 AM, Roberto Ierusalimschy wrote:
On the other hand, it would be strange to allow nil environments in
userdata but not in other types. And it would make setfenv/getfenv
more complex.
"Other types" in this sentence means "functions". No types other than
functions and userdata have environments. I argue in
<http://lua-users.org/wiki/UserDataRefinement> that the two concepts
are radically different. So in my opinion, the complexity of
setfenv/getfenv is the result of trying to make the same API do two
different things.
Why does a userdata have an environment? A userdata is never the head
of a call frame; it's only used as the self (or other) argument of some
function, either a metamethod or a method function. And that function
already has an environment. It can only get at the environment of the
userdata indirectly, by pushing it onto the stack and then doing a
lookup.
It's obviously useful that userdata have associated tables, I'm not
arguing against that. But calling the associated table an "environment"
table and then forcing it to have similar semantics to an unrelated
concept (function environment tables) is just confusing (and, I would
argue, inefficient for user code).
It is this line of reasoning that leads to me suggest renaming them
"peer tables", and at the end of the Wiki musings, to suggest adding an
additional "C peer" to the userdata structure. I think this nicely
captures the semantics: a userdata is a bridge between the Lua and the
C worlds; it has a peer on each side, as it were. (Or should that be
pier? Mixed metaphors are so tempting.)
All of the use cases I could come up with for userdata environments
have to do with storing instance-local data in them, which is why I
ended up with the convention that the member function (or metamethod)
environment table contains the type-common data, which is either the
metatable for the userdata or some table found in the metatable, such
as its __index table. This usage is consistent with member functions
and metamethods for ordinary Lua tables: the metatable of the Lua table
contains the type-common data, and the table itself contains the
instance-local data.
Looked at that way, the "environment table" of a userdata is the
equivalent not of the environment table of its associated functions,
but rather the equivalent of the "table itself" of a Lua table. So
logically the API function which I called lua_getpeer should return:
for a userdata, the peer table
for a table, the table itself
for anything else, nothing.
This is not quite accurate, though: in the case of a Lua table being
used to implement a different datatype, the Lua table is indeed the
instance-local store, but you have to use rawget/rawset on it to get at
the fields, since gettable/settable are presumably being overridden to
provide a different view. There's no such thing as a "raw table" (that
is, an object which would have the raw view of a metatable'd table), so
the only option is to adopt the convention that lookups in the peer of
an object must be done with the lua_raw* functions.