Page 1 of 1

What is the connection between the __index meta method and meta tables?

Posted: Wed Nov 03, 2021 7:41 am
by ddabrahim
Hi.

I trying to learn how to do OOP in Lua and while I did find working solutions, I don't actually understand how it works. So I am trying to better understand why and how things works in Lua. But I have a hard time with it.

Could someone please help me explaining the relationship between the .__index meta method and the setmetatable() method?

I did read documents and watched tutorial videos including the one from Jeeper but I just don’t understand it.

This is what I am trying to do to help me understand:

I have 2 tables

Code: Select all

human = {age = 12}
student = {grade = 5}

student.__index = student
setmetatable(student,human)

function love.draw()
  love.graphics.print(""..student.age)
end
It works, but I do not understand why.

I understand I set human as the parent table of student and so student can access the age property of human.
What I don’t understand however is that I need to set the .__index of student to point to it self in order for this to work. I simply can not figure out why do I have to do this.

Some tutorials saying if I would do

student.__index = human

On it’s own it would point to the table human and I could call student.age but it is not true. For this to work I need to setmetatable(student,student) and then I can call student.age which is just even more confusing.

Could anyone please explain it in layman's terms what is happening here, what is the connection between .__index and setmetatable()?

Thank you in advance.

Re: What is the connection between the __index meta method and meta tables?

Posted: Wed Nov 03, 2021 8:51 am
by ReFreezed
I think some confusion is caused by some tables being reused for multiple things. I'll try to explain how things work.

Code: Select all

do
	-- This object will represent the Human "class" and has all "fallback"
	-- values for table fields that human instances don't have.
	local Human = {age=12} -- This will be the __index metamethod.

	-- This will be the metatable that human instances will use.
	local HumanMetatable = {__index=Human}

	-- Here's an instance of our Human class. It's just a normal table so far.
	local someHuman = {}
	print("before setmetatable:", someHuman.age) -- nil, as we haven't set the metatable yet.

	setmetatable(someHuman, HumanMetatable)
	print("after setmetatable: ", someHuman.age) -- 12

	-- Because someHuman doesn't have an 'age' field (i.e. it's nil) Lua falls
	-- back to using the __index metamethod of the metatable (i.e.
	-- HumanMetatable.__index) to determine what value someHuman.age should
	-- return.

	-- Note that Lua simply returns HumanMetatable.__index.age here, i.e. Lua
	-- does a normal table lookup for an 'age' field in the table
	-- HumanMetatable.__index. (This will be important when we extend the
	-- Human class later.)
end

do
	-- As you see above, we're using two tables for the meta-functionality of
	-- our "human" objects - Human and HumanMetatable. This is not necessary -
	-- we can save a table by using Human as both the __index metamethod and
	-- the metatable itself. (This seems to cause some confusion.)

	local Human   = {age=12} -- Human is both the __index metamethod...
	Human.__index = Human    -- ...and the metatable!

	local someHuman = {}
	setmetatable(someHuman, Human) -- We can now use Human instead of HumanMetatable.
	print("human age:  ", someHuman.age) -- 12

	-- Now, let's extend the Human class with the subclass 'Student'.
	local Student   = {grade=5}
	Student.__index = Student -- Again with the same trick of reusing one table for both the metatable and the metamethod.

	-- We use setmetatable() the same way when we connect subclasses to parent
	-- classes as when we connect class instances to their class. We're just
	-- creating a table lookup "chain".
	setmetatable(Student, Human)

	local someStudent = {}
	setmetatable(someStudent, Student)
	print("student age:", someStudent.age) -- 12

	-- Here are all steps Lua takes to get the value 12:
	-- * Lua looks up someStudent.age - it's nil!
	-- * someStudent has a metatable, so...
	-- * Lua looks up getmetatable(someStudent).__index.age - it's nil!
	-- * getmetatable(someStudent).__index also has a metatable, so...
	-- * Lua looks up getmetatable(getmetatable(someStudent).__index).__index.age - it's 12!
	-- * Lua returns 12.
end

Re: What is the connection between the __index meta method and meta tables?

Posted: Wed Nov 03, 2021 11:52 am
by pgimeno
ddabrahim wrote: Wed Nov 03, 2021 7:41 am

Code: Select all

human = {age = 12}
student = {grade = 5}

student.__index = student
setmetatable(student,human)

function love.draw()
  love.graphics.print(""..student.age)
end
It works, but I do not understand why.
No wonder, because it's not supposed to work. And indeed, when I test it I get:

Code: Select all

Error: student.lua:8: attempt to concatenate field 'age' (a nil value)
stack traceback:
	[string "boot.lua"]:777: in function '__concat'
	student.lua:8: in function 'draw'
	[string "boot.lua"]:618: in function <[string "boot.lua"]:594>
	[C]: in function 'xpcall'
So no, it doesn't work.

See if this is of any help: https://codeberg.org/pgimeno/Gists/src/ ... e-tutorial

Re: What is the connection between the __index meta method and meta tables?

Posted: Wed Nov 03, 2021 6:48 pm
by zorg
To add to ReFreezed's answer above, it can also be a bit confusing that while metamethods would need to be... well, methods (functions), lua allows __index to be a table as well, but it's still called the index metamethod.

Re: What is the connection between the __index meta method and meta tables?

Posted: Wed Nov 03, 2021 8:02 pm
by ddabrahim
Thank you for the replies, much appreciated.
I'll try to explain how things work.
Thanks a lot. The way you explain it and the concept sounds so simple and helped me to see this from a different perspective.
This is the way I see this right now.

Code: Select all

student.age --nil so Lua going to look up the metatable of student.

setmetatable(student, human) --in this case the student has a metatable called human and so Lua go and look up the __index of human

human.__index = human --because the __index of human is point to the human table student.age will return a value  
OR

Code: Select all

setmetatable(student,student) --in this case student is also the metatable and Lua going to look up the __index of student

student.__index = human --because the index of student is point to the human table, student.age return a value
The way I think about this and the way I am describing is probably incorrect, but at the moment this perspective is more simple for me to think about it. I'm going to need some more time to get my head around this but for now I think this new perspective is working...
No wonder, because it's not supposed to work.
Sorry, not too sure why I believed it is working but now using my new perspective, I can see that it is indeed not supposed to.
Thank you for the link I'm going to attempt to learn from it.
metamethods would need to be... well, methods (functions), lua allows __index to be a table as well, but it's still called the index metamethod.
Yes at the moment I am so confused when, why and how to use it. But maybe I am on track now.

Thank you all!