Lua - Metatables with Lists



Metatables in Lua is a powerful mechanism to modify the default behaviours of tables. When we use table to represent a list, we can use metatables to define custom operations to lists.

  • Custom Indexing − We can define custom behavior while handling elements, like handling out of bound conditions, get a different default value instead of nil etc.

  • Operator Overloading − We can define standard operators like +, - , * , .. , = to work seemlessly with lists as these are working with strings.

  • Custom Length Handling − We can control how to decide length of the list especially in case of sparse lists.

  • Read Only − We can make a read only list where no new insertion/updation is allowed to the list.

Setting a Metatable for a list

As a first step, we create a list using a table.

local list = {"apple", "banana", "mango"}

Then we create another table to act as a metatable and associate with the list using setmetatable() method.

local listMetatable = {}
setmetatable(list, listMetatable)

Now we can add metamethods to define the custom behavior of the list.

Example - Using __index() method to handle non-existing key

__index(table, key) metamethod is called when we're trying to use a key which is not assigned in the table. We've implement this method to return a default value for out of bound indices.

main.lua

-- list of numbers
local numbersList = { 20, 10, 30, 40 }

-- metatable to define default values
local metatableDefault = {
   __index = function(table, key)
      if type(key) == "number" and (key < 1 or key > #table) then
         return "out of bounds"
      else
	     -- standard default value
         return nil 
      end
   end
}

-- set the metatable
setmetatable(numbersList, metatableDefault)

-- prints 30
print(numbersList[2])
-- prints out of bounds
print(numbersList[0])
-- prints nil
print(numbersList["a"]) 

Output

When the above code is built and executed, it produces the following result −

10
out of bounds
nil

Explanation

  • numbersList is the list implemented as a table.

  • metatableDefault is a metatable with a metamethod __index() defined.

  • (key < 1 or key > #table) check is to see if index is out of range and then a default value "out of bounds" instead of default nil value.

  • If key is not a number, then we're returning nil as standard default value.

  • setmetatable(numbersList, metatableDefault) is used to set the metatable on numbersList.

  • numbersList[2] returns value at key 2 as table by default uses numeric indexes starting from 1.

  • numbersList[0] returns "out of bounds" (returned by __index() method) as 0 is a numeric key and is not present in our numbersList list .

  • numbersList["a"] returns nil as "a" is a non-numeric key and is not present in our numbersList list and __index() method returns nil for it.

Important Metamethods

Following are examples of important usage of metamethods to perform custom operations on Lists.

Using __newindex() method to make a readonly list

Using __len() method to handle length of list

Using __concat() method to concatenate lists using .. operator.

Using __eq() method to compare lists using == operator.

Using multiple metamethods for a list.

Advertisements