What is self and how can I use it?

So I know Java pretty well, but I am mostly self taught in Lua. Is self in Java similar to self in Lua?

Javaā€™s this which can be used to reference the object in the constructor, is like self in Lua.

3 Likes

In java, something similar to self is this. You can find more informations on google. Then, self and this are not really that same. With this you can call the constructor of a class, what self not can. But the reason why i say that both are similar is this:

local Class = {}
Class.__index = Class

function Class:new(...) --Class Constructor
    local NewClass = {}
    setmetatable(NewClass, Class)
    NewClass.Variadic = ...
    return NewClass
end

--You canā€˜t do: self(...) as with this(...), but you can do self.new(...)
function Class:printVariadic()
    print(self.Variadic) --Its works
end

And this would be in java:

public class Class {
    private string StringTest;
    public Class(StringTest) {
        this.StringTest = StringTest;
    }
    
    Public GetStringTest() {
        return this.StringTest
    }
}

Sorry, i canā€˜t really explain this, you should try yourself. I forgot how java works and idk if 100% what i say is correct, so if you find any problem then write it me! Hope this helps!

1 Like

I do not think metatables are necessary to explain self.

That is true for local variables, but not for values. ... is not a tuple of variables, it is a tuple of values. You can still put it in a table and iterate over it even with over 200 elements.

self can be easily explained just by showing its only automatic use case:

local myTable = {}
function myTable:myMethod()
    -- now `self` exists which is only 1 thing: the first argument of the function call
    print(self)
end

-- when using a method-like call to a function, the value you are calling it from becomes the first argument
myTable:myMethod(true) -- now `self` becomes `myTable` and so it prints `table: 123456789`
myTable.myMethod(true) -- if you do not use a colon (:), it acts like a normal function and so `self` becomes the first argument which is `true` and it prints `true`
myTable.myMethod(myTable, true) -- this is equivalent to `myTable:myMethod(true)

self is automatically created only when you define a method-like function using a colon (:), but you can completely copy the behavior into a normally defined function just by putting self as the first parameter, and it can still be used exactly like a method with a colon (:) if you put it in a table since the way you choose to call the function is the deciding factor to what self becomes.

7 Likes

See:

My statement still stands.

Looks like functions can have a max of 50? Either way there is no reason to have hit this limit.

3 Likes

Thanks, I was unsure exactly but this helped me understand it a lot better. Thanks to you too @XxELECTROFUSIONxX

2 Likes

Fun fact: donā€™t make a function like so:

function module:f()
  print(self:f())
end

It will crash your gameā€¦

3 Likes

Itā€˜s like:

while true do
    print(1)
end

This repeats the same and the same

In your function, you are calling the function, wich calls the function self again, and again, and again. I hope that no ones did itā€¦

3 Likes

Yes.
Itā€™s called a Stack overflow!

3 Likes

So itā€™s called stack overflow :flushed:

7 Likes

also known as a stack overflow :eyes:

1 Like

Hereā€™s an example of self being used in my accounts module:

-- global table
local account = {
	names = {}
};
-- functions
function account:get(name, password)
  if self.names[name] and password == self.names[name][2] then
      return self.names[name][3]  elseif self.names[name] and not password == self.names[name][2] then
	  return nil
  else
     return nil
  end
end
function account:set(truePassword, name, password, data)
   if self.names[name] and truePassword == self.names[name][2] then
       self.names[name] = {name, password, data}
   else
      print('set failed')
   end
end
function account:setraw(name, password, data)
    self.names[name] = {name, password, data}
end

function account:getraw(name)
	if self.names[name] then 
          return self.names[name][3]
	else
		return nil
	end
end

function account:clear(name, password)
   	if self.names[name] and password == self.names[name][2] then 
        self.names[name] = nil
	else
		print('clear failed')
	end
end

function account:clearraw(name)
   	if self.names[name] then 
        self.names[name] = nil
	else
		print('clear failed')
	end
end

return account


Basically, self is just the table being returned. Of course, since iā€™m only using methods (tbl:function instead of tbl.function), I donā€™t need to define self in the first place.

3 Likes

This very cool gamer explains what self is in this video!

4 Likes

I still donā€™t get why in some lua OOP you do

local t = {}
t.__index = t

like this one.

Iā€™ve been trying to figure it out but my brain keeps having a meltdown. Yes I know that if t.x is nil then t.__index comes next which is t, but thatā€™s completely pointless because itā€™s just going over the same table. I thought it was used because of self I guess it wasnā€™t.

1 Like

Yea I donā€™t understand that too. Iā€™m guessing itā€™s just creating the new index metatable. So it detects if the item being added to the table isnā€™t already there, then if itā€™s not, add it in.

Because table.insert could insert the same item twice instead of replacing it, Im assuming.

This is not pointless when youā€™re using it as a constructor (there are many other optimal ways to use __index, but considering all of the examples above, I will dissect precisely what is happening in their typical examples, then showcase other examples where the __index metamethod will be used.

Consider this script for instance:

local SomeModule = {}
SomeModule.__index = SomeModule

function SomeModule.new()
	local self = {}
	self.ThingToPrint = ""
	return setmetatable(self, SomeModule)
end

function SomeModule:SetThingToPrint(NewThingToPrint)
	self.ThingToPrint = NewThingToPrint
end

function SomeModule:GetThingToPrint()
	return self.ThingToPrint
end

function SomeModule:PrintThing()
	print(self:GetThingToPrint())
end


local PrinterOne = SomeModule.new()
local PrinterTwo = SomeModule.new()
local PrinterThree = SomeModule.new()

PrinterOne:SetThingToPrint("Hello!")
PrinterTwo:SetThingToPrint("Goodbye!")
PrinterThree:SetThingToPrint("Some greeting!")

PrinterOne:PrintThing()
PrinterTwo:PrintThing()
PrinterThree:PrintThing()

If you run this code, this will print the following:
image

Behind the scenes, this is what is happening:

I will use PrinterOne as an example, though PrinterTwo and PrinterThree will undergo the same exact processes.

Letā€™s start with

local PrinterOne = SomeModule.new()

SomeModule is a reference to the actual SomeModule table, which would look something like this:

(I have cleared out the functions in this case to make it more clear to read):

local SomeModule = {
    ["new"] = (function() end)
    ["__index"] = {
        ["new"] = (function() end)
        ["__index"] = . . . (SomeModule)
        ["SetThingToPrint"] = (function() end)
        ["GetThingToPrint"] = (function() end)
        ["PrintThing"] = (function() end)
    }
    ["SetThingToPrint"] = (function() end)
    ["GetThingToPrint"] = (function() end)
    ["PrintThing"] = (function() end)
}

Notice that __index references itself, therefore the table inside of __index will always be itself continously forever, hence why it is represented with . . .

Okay. So weā€™ve called SomeModule.new() and set its return value to equal PrinterOne.

As we call SomeModule.new(), internally, the execution instruction set goes through line by line of SomeModule.new()

It first creates the self table, which is empty and not populated at the time.
image

Then, it populates ThingToPrint inside of self as an empty string value.
image

Lastly, it returns the metatable of self to SomeModule. In order to do so, the instruction set needs to index SomeModule. It first checks whether the __index metamethod is valid. Because our __index metamethod is, in fact, valid, it points to SomeModule["__index"] instead of directly SomeModule.

This will allow us to get copy the self value that was created in the module over to PrinterOne, and us being able to independently change self for every time we call SomeModule.new() without having the values associated in self being overwritten.

It would be bad if we wanted to do this without an __index metamethod:

local PrinterOne = SomeModule.new()
local PrinterTwo = SomeModule.new()

as later in our code, we wonā€™t be able to seperate our changes from PrinterOne or PrinterTwo.

Consider this example without __index, for instance:

local SomeModule = {}

function SomeModule.new()
	local self = {}
	self.ThingToPrint = ""
	return self
end

function SomeModule:SetThingToPrint(NewThingToPrint)
	self.ThingToPrint = NewThingToPrint
end

function SomeModule:GetThingToPrint()
	return self.ThingToPrint
end

function SomeModule:PrintThing()
	print(self:GetThingToPrint())
end


local PrinterOne = SomeModule.new()
local PrinterTwo = SomeModule.new()
local PrinterThree = SomeModule.new()

PrinterOne:SetThingToPrint("Hello!")
PrinterTwo:SetThingToPrint("Goodbye!")
PrinterThree:SetThingToPrint("Some greeting!")

PrinterOne:PrintThing()
PrinterTwo:PrintThing()
PrinterThree:PrintThing()

The defintions for PrinterOne, PrinterTwo, and PrinterThree would run fine but the moment it hits the methods, it will error.

This is because we no longer have access to the methods of SomeModule, only whatever is inside of self, which is ThingToPrint.

With or without __index, you will always end up having this:

local PrinterOne = {
    ["ThingToPrint"] = ""
}

(Remember the instructions that are being made are roughly this):

local PrinterOne
PrinterOne = SomeModule.new() = self = {
    ["ThingToPrint"]
}

__index allows PrinterOne to access and call methods from SomeModule without filling its table. You can now probably see why its problematic if you leave out the __index.

After called SomeModule.new() and setting it to the value of PrinterOne, we then call PrinterOne:SetThingToPrint("Hello!")

By this point, we have already defined PrinterOne, PrinterTwo, and PrinterThree, and SomeModule as follows: (note that the memory address of SomeModule is exactly the same as all __index's present inside of SomeModule, as it is a reference of itself.)

image

Continuing on, "Hello!" gets passed through as NewThingToPrint = "Hello!" and self.ThingToPrint = NewThingToPrint (Note that the memory address of self does not point to SomeModule, but points to PrinterOne.)

image

This calls the function PrintThing from SomeModule. PrinterOne is a table, similar to SomeModule, However, PrinterOne does not actually contain any functions inside of it. PrinterOne is a table consist of only ThingToPrint inside of it.

Then it continues doing the same operations for the other method calls:
image

image

image

Then finally, the PrintThing() method is called (which just prints self.ThingToPrint)

image

image

image

Let me know if you have any questions on this, Iā€™ll be happy to break it down even further.

6 Likes

Oh I see! So the __index metamethod used in this way is basically a gateway that allows you to run functions/methods that doesnā€™t exist in self from a separate table. If we were to just do a for loop to copy all the methods and return it it would mean that we could potentially overwrite the methods and break it. And because itā€™s returning a reference point to the methods, all the other objects that were created before it could have its own methods overwritten.

In short term, every object created under a class can access that classā€™s special methods without writing to it.

I see the importance of it now, and Iā€™m really thankful that you explained it thoroughly. I do have one question though, is that thing you used to display the table a Call Stack?

You can also do this

local mod = {}
mod.__index = mod

function mod.new(Player)
local self = {} -- create a dictionary for self
self.Backpack = Player.Backpack
return setmetatable(self, mod) -- return self in a metatable 
end

function mod:AddToolToInventory(Item) -- just a little test function 
Item.Parent = self.Backpack
end
return mod


-----------------Server

local UT = require(MODULE_PATH)
local Players = game:GetService("Players")
Players.PlayerAdded:Connect(function(Player)
local _Utilis = UT.new(Player)
local example = toolpath:Clone()
_Utilis:AddToolToInventory(example)
end)```
1 Like

A question

If there are more than 1 aruguements passed to this, self will take which arugmenet?

For what Iā€™ve seen is that self takes the first argument no matter what, so if you were to do someObject.Method(self) and pass ā€œamogusā€ as self, then when youā€™re referencing self itā€™d be the string ā€œamogusā€

i.e print(self) ā†’ ā€œamogusā€

though you can test this on your own.

1 Like