Lua Metatables
Archive - Originally posted on "The Horse's Mouth" - 2010-04-02 07:31:14 - Graham EllisIn a recent article ([here]), I asked if Lua was an object oriented language - and I concluded that it supports the OO paradigm very well, even though it doesn't have keywords like "class", "private" or "extends". At the end of that article, I mentioned metatables - a subject I have carefully sidestepped so that I could show you the basics first. But Metatables are a vital piece of making Lua work "OO".
So - what is a metatable?
It's a table that you can add in to another ("regular" ?) table that adds members / characteristics to that original table - makes it do things differently.
For example, if you define a __tostring method in a metatable, then set that as the metatable for a regular table, you'll change how Lua prints out that table. I've put an example showing the mechanism [here].
How does a metatable work in an OO like way?
Let's have an example:
• set up a table that contains the common code for all objects of type "pet"
• set up a metatable that contains __string (and other stuff) which describes how a table that's set up to contain a pet performs its actions
• add a function (a constructor) which sets up another table for each time you call it - to contain all the data about a particular pety, and have that table reference the metatable just mentioned
You can then write common code to use pet objects ... the main application may be as simple as:
gypsy = pet.new("dog",28,3)
charles = pet.new("prince",95,60)
print (gypsy)
print (charles)
and it runs like this:
[trainee@holt lm10]$ lua metasmall
A pet of type dog aged 3 weighing 28
A pet of type prince aged 60 weighing 95
[trainee@holt lm10]$
The commented source showing you how to do that setup is [here].
How do I override operators?
The effect of adding a Gypsy to a Prince is a muddy mess - any you would like to do that using the "+" operator.
In Lua, you can override the standard operators for a table using functions such as __add in their metatable.
Add the code
ff = gypsy + charles
print (ff)
onto the end of the previous example, and run it:
[trainee@holt lm10]$ lua metapetite
A pet of type dog aged 3 weighing 28
A pet of type prince aged 60 weighing 95
Muddy mess
[trainee@holt lm10]$
The full code (showing the addition of the __add function) is [here]
Can I user methods to a series of tables?
The examples so far have overridden the behaviour of operators and standard functionallity - but you can also add your own. If you call a function that does not exist on a table, the __index function within its metatable is called up to resolve what code is to be run.
The first parameter to __index is the table on which the method is being called, and the second parameter is the name of the function needed. The __index method should return the function that's to be run (very often, that will be a function in the metatable itself, or in the pet table), or a nil if the requested function does not exist.
There's an example [here] - adding a getliftable method which returns a true or false value. The calling code looks like this:
gypsy = pet.new("dog",28,3)
charles = pet.new("prince",95,60)
family = {gypsy, charles}
for _,v in ipairs(family) do
print (v)
if v:getliftable() then
print ("Can lift ")
else
print ("is very heavy")
end
end
And when you run it, you get
[trainee@holt lm10]$ lua metamid
A pet of type dog aged 3 weighing 28
Can lift
A pet of type prince aged 60 weighing 95
is very heavy
[trainee@holt lm10]$
How does it decide whether to return true or false? Ah - that's the beauty of encapsulation which hides code within a set of fuctions that the higher level user calls.
Does getliftable always runs the same code? Not necessarily - if you had not only pets but also some other type of tables, Lua will select the appropriate code to run depending on the data type. That's polymorphism.
So - although you don't have all the usual keywords associated with an OO language, you do have the key feature / benefits - encapsulation, polymorphism, separation of detailed code for higher level user (I need to add some "local" declarations for better partitioning!) ... so I'm going to say - with confidence - that you can write code using OO techniques in Lua.