Inquiry On Correct Module Script Notation

Hello,

Below is a module script I have for a game I’m working on. The module script is very basic and its used for loading and removing maps from the workspace. When should I use the : notation for the methods within the module script (I.e. function module.loadMap(name) vs function module:loadMap(self, name)

Module Script
local ServerStorage = game:GetService("ServerStorage")

local module = {}

module.CurrentMap = "nil"
module.IsMapLoaded = false

function module.loadMap(name)
	local clone = ServerStorage.Maps[name]:Clone()
	clone.Parent = game.Workspace
	module.CurrentMap = name
	module.IsMapLoaded = true
end

function module.removeMap(name)
	game.Workspace[name]:Destroy()
	module.CurrentMap = "nil"
	module.IsMapLoaded = false
end

return module

Thank You!

For me I think its better to use : when calling a function and . when getting like a property or something otherwise it doesn’t really matter.

1 Like

Yes I was thinking that as well. Not sure why Lua has to make a different notation for properties vs methods though because I much rather prefer other programming languages (such as Java, Python, C++, etc) where properties are accessed as Object.property and methods are Object.method() but if other developers and Roblox use this convention for classes then I guess I will too for readability.

using . to call a function looks a bit ugly to me lol its fine when wanting to refence something though.

The colon : has a very special place in Lua. It provides the table the called function belongs to as the self argument. self is probably like this in JavaScript.

The two functions in the below example give equivalent results.

-- Module

local module = {}

function module.DoThis(self, arg1)
	print(self, arg1) --> module table, "Forest"
end

function module.DoThisWithoutSelf(arg1)
	print(self, arg1) --> nil, "Forest"
end

function module:DoThat(arg1)
	print(self, arg1) --> module table, "Forest"
end

return module

-- Script

local module = require(script.ModuleScript)
module.DoThis(module, "Forest")
module.DoThisWithoutSelf("Forest")
module:DoThat("Forest")

Colon is especially common in OOP (object oriented programming), because it enables us to send a reference of the object into a function that is part of the object’s class.

Using the colon in examples like yours doesn’t bring any but aesthetic benefits, since self is essentially module. On the contrary, more memory is used because the whole module is pushed into the function as an argument.

“Classes” in OOP are a different story, because the object itself doesn’t necessarily contain the functions developers interact with. Instead, they are contained as part of the class and accessed via metamethods. “If a function is not found in self, look into the class before returning nil or raising an error.”

-- Module

local Map = {}
Map.__index = Map

function Map.new(mapName)
	local self = setmetatable({}, Map)

	self.MapName = mapName

	return self
end

function Map:PrintMapName()
	print(self.MapName)
end

return Map

-- Script

local MapClass = require(script.MapClass)

local Map = MapClass.new("Forest") -- .new() is a standard constructor name
Map:PrintMapName() --> "Forest"

If you find the OOP useful, it can prove itself as a great paradigm. As any paradigm, it has certain disadvantages, but the benefits mostly outweight the cons in my use cases, therefore I rely on it regularly.

In your example, the map module could be a class too. The constructed object could contain multiple other objects (focusing on composition over inheritance) and have different functions to add behaviour. The final function could be a destructor to clear the map and disconnect signal connections.



Once you dive a little deeper, you will notice C++ has a lot to do with objects, and the whole Roblox game data model consists of objects (instances).

3 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.