Main Content

Lua Metatables

Archive - Originally posted on "The Horse's Mouth" - 2010-04-02 07:31:14 - Graham Ellis

In 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.