Whats the point of metatables?

So far, all i see people using Metatables for is the __index which seems useless.

I am new to Metatables but from what ive seen i don’t see use in them. Anyone got any uses or times they have come in handy?

1 Like

It’s used to return values and tables.

Honestly in my opinion, a lot of it can get messy real fast, so it isn’t (again in my opinion) super ideal in most cases to use, nor necessary.

However, there’s some cases that they shine. One of the most common uses I’ve seen is to emulate an OOP system, which means object-oriented programming.

If you’re not familiar with OOP, I’d recommend checking it out. Lua is not object-based, but with the use of metatables, you can emulate an OOP language, in a sense.

Here’s a practical example:

local potato = {} --//Let's make a new "potato" class.
potato.__index = potato --//The __index metamethod allows us to index the specified table if we first index something nil within "potato" (or the table that contains this metamethod)

function potato.new() --//"Constructor" for creating a new object of our 'potato' class.
local this = setmetatable({}, potato) --//I don't want to lose you here. The first argument, being the empty table, is returned that has a set metatable to it, which is potato, and all of the metamethods from within potato are applied to it.

this.color = "Brown" --//This is where it gets fun. You can define all the fields you want for the object that makes up a 'potato'. You can even add new fields for unique instantiations of the 'potato' class, or so fourth.

return this --//Return the newly made potato object
end

function potato:Eat() --//We can define methods created objects can index, but aren't directly stored in those objects
print"somebody ate me!"
end

--//Let's create a new potato we can eat!
local myPotato = potato.new()
--//Now let's eat it!
myPotato:Eat()
--//Yum! But I want another one...not a problem!
local mySecondPotato = potato.new()
--//Now let's eat this new one.
mySecondPotato:Eat()

--//Keep in mind myPotato and mySecondPotato are two different table objects, but they both utilize the  same :Eat() method, which is NOT stored directly in the objects returned from our potato constructor. (The .new function at the top)

Hopefully this cleans up the concept a bit. I can break down anything I might’ve not specified super clear. :slight_smile:

6 Likes

Do they have any use outside of OOP?

Its to make things look way more complicated and make people look 10x more advanced than they really are

5 Likes

I also dont see why you would need metatables at all. They dont look… useful.

1 Like

Another use is operator overloading, if you wanted to allow arithmetic operations on table objects. Which again, not super necessary and can get messy pretty quick depending on your use.

1 Like

So like __divide or something?

OOP approaches can actually be really helpful. Do you have to use an approach like this? Nope. But in certain cases, it makes your code cleaner and easier to manage.

For example, say we’re making an enemy system for a game. I could see an OOP approach being really neat for that, as we can easily manage enemy info on a per-enemy basis, and we can also create a large number of enemy objects for our game. You can even implement a method called every frame to update each individual object, which I’ve done before, makes it easier to manage.

1 Like

How does this include metatables? All I see in this post is OOP.

Why so salty?

Metatables and metamethods are what allow you to use an approach like this, which I think I explained fairly well

You’re asking for use cases, and I gave you the most common.

1 Like

Oh I didnt mean to come off as salty.

There are many uses. You can read the lua docs for metatables and metamethods here: Programming in Lua : 13

That section goes through all the metamethods, with examples of use cases for each.

You don’t have to use them, obviously, but they have use cases.

2 Likes

What’s the point of using __index, why not just make the function return an actual copy of the table?

function newpotato() --//"Constructor" for creating a new object of our 'potato' class.
    local this = {}

    this.color = "Brown"

    function this:Eat()
        print"somebody ate me!"
    end

    return this --//Return the newly made potato object
end
1 Like

That’d be a huge waste of memory per-object. __index allows you to access a specified table if a field is nil within the object itself.

2 Likes

I never actually understood why people set a tables index to itself.

I don’t understand how that uses any more memory. Either way you’re creating a new table, the only difference is that one inherits its properties from another table and the other’s properties you manually define in the function. Either way you’re creating a new table with its own properties that need to be stored in memory.

1 Like

You could either store methods directly inside each object you make, or, index those methods using a metamethod within your “class” table directly.

If we have like 1000 of these objects stored in memory, I’d bet there’d be a significant difference depending on how complex those methods would be

Not saying it’s bad, but I don’t think I’ve ever seen some one use an approach like that

4 Likes