Is **self** used just to make code look better?

Hello, so I know what self does, but I’m wondering if it is just used to make code look better/more advanced.

The following functions do the same thing:

local function PrintName(Part)
    print(Part.Name)
end
local function Part:PrintName()
    print(self.Name)
end

Is there any scenarios where I shouldnt be using self or is it just preference?

2 Likes

I’m new to scripting (half-a-year) but relatively good, but it seems apparently not cause I don’t know.

I’m genuinely confused. I feel like it has a meaning though, maybe not.

1 Like

Wait how you do that ? i would like to know because its not working for me and that looks interesting

2 Likes

According to Bing AI @pf_z1 :

In Roblox Studio, .self is often used in the context of Object-Oriented Programming (OOP). It’s a way to reference the current object or table you’re working with.
Source: Conversation with Copilot, 6/22/2024 16:01

1 Like

self is typically used in Object-Oriented Programming and OOP-related programming to refer to the current object you’re working with. It’s not just specific to Luau, it’s widely recognised in this context even for languages that don’t promote such a name (e.g. Python, there is no keyword for self). I like to think of it as a standard.

I like to follow this rule when programming;

  • your code should be well enough commented and readable enough so that if another programmer read it they would know what the code is doing.
  • self adds to this as it tells someone you are working with a class or object.

TLDR: self helps to differentiate objects of classes from other variables or constants.

2 Likes

Basically what I said, just way better.

2 Likes

self is used in OOP. It is just the current object.

local MyClass = {}
MyClass.__index = MyClass

function MyClass:frumbicate()
    explode(self)  -- self is the current instance of MyClass
end

Though I usually write it like this because Luau sometimes gets mad at me in strict mode:

function MyClass.frumbicate(self: MyClass)
    explode(self)
end

They are equivalent.

2 Likes

To answer your question super simply, yes, it is syntactic sugar. However, you should use it both in declaring and consuming member functions on classes in Lua / Luau.

Let’s explore this a little in a small class example in Luau. Because Part is actually a Roblox Instance name / primitive type, it’s not a valid example class, so let’s name our class MyPart.

ModuleScript MyPart

--!strict
export type MyPartType = {
    __index: MyPartType,
    new: (name: string) -> MyPartType,
    PrintName: (self: MyPartType) -> (),
    Name: string,
}

local MyPart: MyPartType = {} :: MyPart
MyPart.__index = MyPart

function MyPart.new(name: string): MyPartType
    local self: MyPartType = {} :: MyPartType
    self.Name = name
    return setmetatable(self, MyPart) :: any
end

-- function MyPart.PrintName(self: MyPartType) would also work.
-- Generally avoid using . instead of :
function MyPart:PrintName()
    print(self.Name)
end

return MyPart

Script consuming MyPart. Put this under the same parent.

--!strict
local MyPart = require(script.Parent.MyPart)
local myPart = MyPart.new("test")
myPart:PrintName() -- Just like in declaration.
myPart.PrintName(myPart) -- this also works
-- again, generally avoid . vs : for member functions

Note that if you use strict in the consuming code, it enables checks like the following in Studio:
image

Another Studio feature - automatic completion on : methods:

vs . members:

5 Likes

Yes and no,

self does represent the Object, however in Object oriented programming, self represents the current object, where as in your case Part is the module.

When firing self:PrintName() you pass on the entire object.

When firing Part:PrintName() you only pass on self vars/properties defined in the context/environment. Meaning self properties declared in other functions will not be passed onto the function it’s firing to.

So it’s very important to not mix them up.

1 Like

Think of it as Part is the Object, By default it includes the functions, __index, and if you have one ClassName/SuperClass, no variables are directly on Part, Part represents the entire module, so think of it as Global.

self is the current Entry under the object, it’s the __index, hence Index representing the index of an array/table. self inherits from the Global Object Part, but it has it’s own properties.

So part looks like:

local Part = Class:Extend("Part")
--- Here's what it looks like
{
__index = ...,---infinitely many (known as Cyclic)
ClassName = "Part",
SuperClass = ...,-- some people have one others don't
...all other Functions, like New/OnNew
}

self looks different, It’s apart of that infinite cycles under __index, self only inherits the Functions. Self is basically like an entry under Part.

self -- Defined when inherited using :

function Part:OnNew()
 self.Base = self.Base -- Something passed through, ex: require(...):New{Base = Part}
 self.BaseName = "Part"

end

--- Now if you were to print self inside OnNew, you'd get

{
   ["OnNew"] = function: 0xf3fc2041e3be0bf83z, -- Function id, representing OnNew.
   ["Base"]  = Part, -- "BasePart"
   ["BaseName"] = "Part"
}

Hope this helps!

1 Like