Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
3

Metatables, what are they needed for?

Asked by 5 years ago

I've been studying metatables on the wiki since day, i understood most of the things unless for some parts; The only thing that i can't really understand is what are they needed for? I heard that they are needed to create classes, if so, what are classes needed for?

0
OOP User#23365 30 — 5y

2 answers

Log in to vote
7
Answered by 5 years ago
Edited 5 years ago

What are Metatables?

Metatables, despite what most people say, are not as complicated as they seem at first glance. People like to describe them as tables within tables, or tables you can attach to other tables, but I personally don't like these descriptions because they don't seem very clear and they certainly don't emphasize the power of metatables. If I had to sum them up in my own words, I'd describe them as such:

Metatables allow you to monitor what is happening to a table. Is it gaining a new entry? Is it invoking tostring? Is it trying to be called as a function t = {}; t()? Etc.

Metatables exist to allow the developer to run code when an action is performed on a table.

Some people will also compare metatables to behaving like an event listener for a table, which is not a bad comparison so long as you know the differences between the two. Now, let's move on to how they're used.

Constructing Metatables

There is a built-in function in Lua called setmetatable -- this function is the only reason metatables exist. You can think of setmetatable as a constructor for creating metatables. However, the function itself only takes two regular tables as it's arguments. Here's an example:

setmetatable({}, {})

The first argument is the table that will keep it's regular behavior, and the second argument is the table that will now behave as the first table's metatable. Here's a more explicit example:

local t = {} -- table
local mt = {} -- table that will behave as t's metatable

setmetatable(t, mt) -- construct the metatable

Once setmetatable is called, it will return the table it was given as the first argument. In this case, it will return t. However, t now has a metatable attached to it. In this case, the metatable is mt.

This is why people describe metatables as "tables you can attach to other tables," but do you see how much explaining had to be done before we reached that conclusion? Not the best beginner-friendly description in my opinion.

Metamethods

Now that you hopefully have a better understanding of metatables, let's table about metamethods. Metamethods are the fields that go inside of the metatable to control what happens in your program when some action is performed on a table you set a metatable to.

If you like the event listener analogy, you can think of metamethods as events that fire when specific actions are performed to on table.

Here is a list of some available metamethods in Lua 5.1:

  • __index
  • __newindex
  • __mode
  • __call
  • __mul
  • __div
  • __mod
  • __metatable
  • __tostring
  • __len

... and the list goes on.

Don't worry; you don't have to know them all by memory. Most of these metamethods are rarely used anyway. The most frequent metamethods you will probably use are: __index, __newindex, __call, __metatable, and __tostring.

The rest you'll only use if you're doing something incredibly specific. But anyway, these are the fields that you will set in your metatable. Let's try __index, for example:

local t = {}
local mt = {
    __index = function(t, key)
        return "the index " .. key .. " does not exist"
    end
}

setmetatable(t, mt)

The __index metamethod will be invoked when you try to retrieve a value from a table with an index that does not exist in the table. __index can be a table or a function, but I won't go in depth about the description of each metamethod in this answer. You can easily find those anywhere online.

Anyway, in this case, when __index is invoked, it will provide you with the index that you searched t for. This value is the second parameter of the __index function in the example above (the key parameter). Let's watch put it to use:

local t = {}
local mt = {
    __index = function(t, key)
        return "the index " .. key .. " does not exist"
    end
}

setmetatable(t, mt)

print(t.someKey) --> the index someKey does not exist

We indexed t for "someKey", but since that index does not exist, Lua checks to see if t has a metatable with an __index metamethod. If so, return whatever __index returns.

Conclusion

This was a very lengthy answer, and it only scratches the surface of how metatables can be used. My goal here was to broaden your understanding of metatables and metamethods more than covering some examples of classes or however else they can be used. If you'd still like an explanation on that, let me know and maybe we can chat through discord.

Hope this helped!

External Sources

Ad
Log in to vote
1
Answered by 5 years ago
Edited 5 years ago

Metatables


As you probably know by now, any table can be used as the metatable of another table, or even multiple tables at once, which is how you can use some of the mathematical metamethods.

To set a table as the metatable of another table you would use the setmetatable(tbl,metatbl) function, and to get the metatable of a specific table, you would use the getmetatable(tbl) function.

Metatables by themselves aren't really that useful, what makes them useful are stored inside them.


Metamethods


What makes a metatable useful are its metamethods, such as __add or __mul for mathematical operations. Or the famouse __index, used in object oriented programming , or OOP.


Mathematical metamethods


Arithmetic metamethods constitute of __sub , __mul, __div,__add and etc. They can be especially useful for combining sets of numbers. A simple example of the __add and __mul would be:


local function add (a) local sum = 0 for _,v in pairs(a) do sum = sum + v end return sum end local meta = { __add = function(a,b) local s1,s2 = add(a),add(b) return s1 + s1 end, __mul = function(a,b) local s1,s2 = add(a),add(b) return s1 * s1 end } local tbl = {1,2,3} local tbl2 = {4,5} setmetatable(tbl,meta) setmetatable(tbl2,meta) print(tbl * tbl2)--36 print(tbl + tbl2)--12

__index


the __index metamethod is one of the most useful metamethods there is, being especially useful for OOP

The index metamethod can be either a function or a table

Ex function:

local meta = {
    __index = function(tbl,key)
        return "attempted to index key: "..key
    end
}

local tbl = {1,2,3}

setmetatable(tbl,meta)

print(tbl["ree"])--attempted to index key: ree

Ex table:

local meta = {
    __index = {ree = "hey, it exists"}
}

local tbl = {1,2,3}

setmetatable(tbl,meta)

print(tbl["ree"],tbl["nope"])--hey it exists, nil

OOP example:

local Account = {balance = 0}

function Account:new (o, name)
  o = o or {Name=name}
  setmetatable(o, self)
  self.__index = self
  return o
end

function Account:deposit (cash)
  self.balance = self.balance + cash
end

function Account:show ()
  print( self.Name, self.balance)
end

local acc = Account:new(nil,"Builderman")
acc:deposit(500)
acc:show() -- Builderman, 500

Now as you can see, despite the acc table itself only containing the "Name" key, it also has access to the deposit and show, and new functions due to it having the "Account table" as a metatable

When the index metamethod of a metatable is an actual table, if you tried to print something in the original table that results in a nil value, it will then look in the __index metamethod of the original table's metatable for a value corresponding with the key, if it isn't nil, it will return that value, if it is nil, it will just return nil


References:


Hopefully this helped!

If there is anything i got wrong or missed, please leave a comment

Answer this question