lua-users home
lua-l archive

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


On 2016-11-10 16:12, Luiz Henrique de Figueiredo wrote:
> What use do you have in mind for this?
> What problem would it solve?

At the lowest level of this, many people seem to write __tostring
metamethods that generate "__name: address" instead of "type: address",
so this would move code repeatedly written by many to a single place.

But why do they do this?

The way I understand the underlying problem, we have few primitive types
("notation" - how things are written/constructed), but many "conceptual"
types ("intention"/meaning - what those constructions represent).  As an
example, there's just number, table, userdata, ... but we might have
concepts of "small" numbers, bignums, rationals, complex, vectors, ...

In different contexts, you want different information. Getting "table
expected, got table" would be useless, "vector expected, got rational"
is better.  But in the interplay of libraries you might also end up with
"complex expected, got complex" where "table expected, got userdata"
would be more helpful.  (Or maybe you're just not interested in what
it's supposed to represent, just in how it's constructed - so you can
tell whether you can iterate over the thing in question or not.)

As far as I can tell, there's three important components involved here:

 *  the primitive / "raw" type used as "notation"
 *  the "conceptual" / intended type built on top of primitive types
 *  a short ID (hash / pointer / ...) for uniquely identifying values

The latter is important for quick visual inspection of values, when
exploring in the REPL or when debugging data paths.  (For debugging, it
would be preferable to look at the actual data paths, but I know of no
language that provides good tooling for that - so we settle for the
simple kludge of tracking values as they move along those paths, then
inferring the paths from that.)

On the C side, we have:

 *  lua_type providing the primitive type
 *  access to __name for the conceptual type (or use the standard luaL_*
      error reporting functions which do this for you),
    plus a shortcut if you know the expected __name (check REG[__name]
      for equality w/ the metatable, as luaL_checkudata does)
 *  lua_topointer for getting a short ID

For convenience, we also have

 *  lua_tostring, producing type+ID
 *  luaL_tostring, producing type+ID or whatever __tostring defines

On the Lua side, we have:

 *  type() for the primitive type
 *  only unless __metatable is set (or if debug.* is available to work
    around that), access to __name
 *  only unless __tostring was defined, the short ID (and only bundled
    with the primitive type)


Now, Daurnimator proposes changing luaL_tostring to return __name+ID
where possible.  With that change:

On the C side, we have access to type, __name, ID, type+ID, __name+ID
(unless overridden by __tostring).

On the Lua side, we have access to type, __name (unless hidden by
__metatable), __name+ID (unless overridden by __tostring).

So while on the C side this would be mere code-reducing convenience, on
the Lua side this would slightly improve the chance of access to __name.


While it's a move in the right direction, I'm not sure that doing only
this would be a good idea.  It raises the importance of __name on the C
side while leaving it potentially inaccessible (hence necessarily
irrelevant) on the Lua side.  This also causes a lot of workarounds to
be written - checking intended type by metatable identity through a
lookup table, having fields in the values (foo.type = ...), etc.

Getting the ID in Lua is also problematic.  I have occasionally used a
small C stub to make lua_topointer available from within Lua to get it.
Where that wasn't possible, once or twice I even went so far as setting
foo.id = tostring(foo):match": (.*)" before setting the metatable, just
to retain access to the ID.  In his second mail, Daurnimator also
mentioned a clunky "rawtostring" to temporarily revert tostring to its
default behavior.  (I've also written that myself a bunch of times...
note that this also has problems in the presence of __metatable, and the
code as written over there would fail for primitive types because
debug.setmetatable (if available at all) would be needed to change those
metatables.)


So a further improvement might be to add rawtostring:  In C/Lua, you'd
have lua_tostring/rawtostring, luaL_tostring/tostring, and luaL_tostring
is modified to return __name+ID or whatever __tostring says.

C has type, __name, ID, type+ID, __name+ID (unless __tostring).

Lua has type, __name (unless __metatable), type+ID, __name+ID (unless
__tostring) - i.e. now you can _always_ access the ID, but __name may
still be inaccessible.


Adding topointer to Lua's baselib instead, you'd have both type and ID
accessible as components on their own and could build rawtostring if you
need it.


Neither of these improves the __name situation on the Lua side.  So we
have the funny situation where on the C side certain uses of __name are
a de-facto standard, to the point where people request moving them into
the implementation, while on the Lua side __name is mostly unheard of
and potentially impossible to access.

Does anyone have any ideas on that?

-- Marco