lua-users home
lua-l archive

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


On Dec 22, 2009, at 3:35 AM, spir wrote:

> I still have a question about metatables, rather about a language design choice. Metamethods are identified through table _key_ names, which builds an indirection (and imo complicates the model). This in particuliar forces metamethod be held in another table, namely the metatable. Why not, like in several other languages, identify metamethods directly by their _own_ name?
> The result seems to be that metatables must be copied, if only to allow further customization (in addition to other things to do at type/instance creation time). In the simplest case, when copying an object one must not forget its metatable... I'm surprised about the overhead, too, because people in the Lua community seem (even more) obsessed with machine performance.
> Without the indirection introduced by such a model, only generic behaviour methods needs be copied (actually references only, as you say it), not key-pointers to them (in an external metatable).
> I guess there is a certain advantage for Lua's model, but I am unable to find it myself (except for not polluting type/object namespace, but in practice this argument does not hold.)

The fundamentals of prototype-oriented, metatable-based design are probably that you should start out by thinking of your object as having three parts:

	The primary table
	The metatable
	The __index table

More complex objects may have more pieces, but this works to start with conceptually.

The primary table supplies the object's identity and generally contains anything that varies on a per object basis. Generally when working this way, one assumes "encapsulation by convention" -- e.g., using a naming convention for private fields but doing nothing else to keep them private.

The metatable and the __index table supply shared behavior across a clone family -- i.e., the object cloned from a single prototype. The metatable supplies behaviors and fallbacks. The __index table supplies shared constant values and defaults for entries in the primary table. (I'm less fond of the trick of having the __index entry in the metatable point back to the metatable. As far as I can see, all this does is save one table in the system and it does so at the expense of making all of the metamethods available for __index access.)

If you can design your object within this model, then the clone operation just needs to copy the primary table (shallow or deep cloning probably depends on the object and might benefit from a __clone metamethod) and copy the reference to the metatable.

If you want to make structural changes to the object to produce essentially a new clone family, you need to clone the metatable as well and possibly the __index table but generally this should be rare. This is the alternative to inheritance in a prototype-based system.

One could also make changes to an existing object swapping out the appropriate pieces in the process to avoid stomping on other members of its clone family. For example, one could promote all of the fields in an object's primary table to the __index table resulting in a sequence like the following:
	
	-- Make a new copy
	local derived = base:clone()
	
	-- Make changes
	function derived:sayhello() print "I'm derived!" end
	
	--- Push changes from instance to shared space for easier and smaller future clones
	derived:updateindex()

On the other hand, this pattern doesn't work as well for changing metamethods. To deal with that, one probably wants a __newindex handler for the object that will clone and update the metatable in response to certain keys being set. (If metatables are sufficiently protected, it can probably avoid cloning the table if it has never been copied.)

Mark