What does it mean to set table's __index to itself?

I’ve been looking at great community sources on Roblox about OOP-like programming. and often I see stuff like:

local Table = {}
Table.__index = Table

And a .new() function, in which you’d set the this table as a metatable of the object?
I understand that metatable was necessary in order for the object to inherit the methods.

But my understanding was that if you set the methods to the “Table” , you would be able to use them outside of the module script so:

local Table = {}

function Table.doStuff() end

return Table

Other scripts could call the .doStuff()

What happens when you involve metatables, why do I need to __index for scripts to be able to use the methods? Is it to do with metatable inheritance of sorts? It seems counter intuitive to look for values in the same table that returned nil… (edit: by which I mean to look for a nil index in the same table…)

1 Like

You do not. This self.__index = self trick is used in OOP. Unless you’re doing OOP, don’t do it.

Somewhat. Some people just like this way of defining their Class’s methods. Others might directly write to their new object.

local Class = {}
Class.__index = Class

function Class.new()
     local New = {}

     setmetatable(New, Class)

     return New
end

function Class:Method()
     print(self)
end

vs

local Class = {}

function Class.new()
     local New = {}

     function New:Method()
          print(self)
     end

     return New
end

It may be important to note, when a function is called when it’s reference is derived from __index the self variable is the table who had a metatable with the __index value, but not the value of the __index value.

local A = {}
A.__index = A
local B = setmetatable({}, A)

function A:C()
     print(self)
end

A:C() -- will print the memory location of A
B:C() -- will print the memory location of B; despite C coming from A
5 Likes

Some useful links:

Talks more about metatable based OOP
http://lua-users.org/wiki/ObjectOrientationTutorial
http://lua-users.org/wiki/InheritanceTutorial

Still about OOP; talks about metatables still, but not as much.

2 Likes

Ok, with your example:

Say in a new script you would do local newClass = Class.new() and then you did newClass:Method()… so it looks for :Method in the newClass, doesn’t find it, goes to class, and presumably finds it… But how does that make sense? What I gather from metatables is that if you don’t find an index in one table, __index makes it to check in the metatable, but if that’s the same table, how come when you do :Method() on Class() it works, but it doesn’t on newClass, which is return setmetatable({}, Class) ? Just looking at the code it seems that you should be able to do:

local Class = {}

function Class.new()
     local New = {}

     setmetatable(New, Class)

     return New
end

function Class:Method()
     print(self)
end

… but if you referenced the return Class directly, you would, indeed, find the method…
… but apparently that’s not the case
I’ll check out the links, thanks.

1 Like

Because you’re setting New’s metatable to Class; New’s __index will also go back to Class; so all methods of Class are instantly “inherited.”

No; you need to some way define a __index back to Class.

Commonly people like to do Class.__index = Class and set New’s metatable to Class because it easily lets you overload other operators too.

local Class = {}
Class.__index = Class

function Class.new()
     local New = {}

     setmetatable(New, Class)

     return New
end

function Class:Method()
     print(self)
end

function Class:__add(x) --// __add metamethod `New` will have now becuase `Class` is New's metatable
     return "i am being added on: " .. x
end

local Object = Class.new()
print(Object + 5)
Object:Method()

This is a good example. But you do understand what I’m confused about? It’s not the inheritance using the metatables (kind of), rather confusion over A already having the method defined, so the A.__index = A seeming redundant. In my mind I should be able to setmetatable({}, A), because A has the method, and __index seeming unnecessary =, because I wouldn’t be adding new functions dynamically, rather just define, but I think I’m starting to understand what’s happening… BTW… are functions indexes, if so, do they have values, or vise versa? Hope this’ll be my last post here …

1 Like

this is the least you can do to make OOP work

local A = {}
local MT = {}
MT.__index = A

function A.new ()
	local self = {}
	setmetatable(self, MT)
	return self
end

If you have understood why, then doing A.__index = A is just a small optimization as it says in the documentation.

Let’s say you create an object

local a = A.new()

then you expect to do this:

a.size = ...
local x = a.position

but you don’t expect to do this:

a.__index = ... 
local x = a.__newIndex

because these indexes are reserved for the functioning of the metatables. That means that these indexes will never be used in your object, nor in your class.

Since the reserved indexes will never be used in class A, and MT will never use other indexes than the reserved ones, then A could also act as MT, A.__index = A. Which is an optimization since you don’t need to create an extra table.

8 Likes