Making a local variable public for other module scripts

I have been creating a simple module loader and I have made a service that uses it. It has some local variables that I need in other scripts, but I’m not sure how to make those accessible to them. In this case I have to make the “world” variable public to other modulescripts.

local RunService = game:GetService("RunService")

local Matter = require(game:GetService("ReplicatedStorage"):WaitForChild("Shared"):WaitForChild("Modules"):WaitForChild("Matter"))

local WorldService = {}

function WorldService:Init()
	self.world = Matter.World.new()
	self._loop = Matter.Loop.new(self.world)
	for _, child in ipairs(script.Systems:GetChildren()) do
		if child:IsA("ModuleScript") then
			table.insert(self._systems, require(child))
		end
	end
end

function WorldService:Start()
	self._loop:scheduleSystems(self._systems)
	self._loop:begin({
		default = RunService.Heartbeat
	})
end

return WorldService

I need full intellisense support, so I need to explicitly declare the type somewhere. Anyone know how to do this? Also, I don’t want to make a getter function

1 Like

I think you could use _G

The mere mention of _G will result in being sent into the badlands, I don’t suggest using _G for anything

1 Like

I can do something similar to this so that autocomplete works, but then roblox still does not have any idea what WorldService.world’s type is.

local RunService = game:GetService("RunService")

local Matter = require(game:GetService("ReplicatedStorage"):WaitForChild("Shared"):WaitForChild("Modules"):WaitForChild("Matter"))

local WorldService = {}

WorldService.world = nil

function WorldService:Init()
	self.world = Matter.World.new()
	self._loop = Matter.Loop.new(self.world)
	for _, child in ipairs(script.Systems:GetChildren()) do
		if child:IsA("ModuleScript") then
			table.insert(self._systems, require(child))
		end
	end
end

function WorldService:Start()
	self._loop:scheduleSystems(self._systems)
	self._loop:begin({
		default = RunService.Heartbeat
	})
end

return WorldService
1 Like

It sounds like you need to define a type for WorldService.

type WorldService = {
   ...
}

local WorldService = {} :: WorldService

The typecast would remove the need to initialize world right away but when you access it from other modules, intellisense should still know what the type should be.

2 Likes

Actually, while we’re at it, is there any reason to differentiate between private and public OOP fields in Lua? In Java it is considered to be essential to do that, but here we trust the client to be responsible enough.

1 Like

We do trust the client to be responsible since there’s nothing stopping them from accessing any variable in the module, but it doesn’t hurt to be explicit with naming conventions.

For me personally, I like to prefix “private” variables with _ (e.g., _world). It lets others and even myself in the future know that we probably shouldn’t reference that variable directly. But that’s just a personal preference and if you find something that works for you, stick with it.

2 Likes

Do you mind explaining why you wouldn’t want to use _G? tia

Yup, these are some pretty good reasons. It’s also very hard to keep track of things you put in _G and where they come from. In other words, it’s disorganized.

Edit: Modules are sort of their own global table. The require function always gives you the exact same table, which is the module being required. But if you are organized properly you shouldn’t need to use global tables

1 Like

Hello again, it seems like intellisense doesn’t work for this solution when using the self keyword. It, for example, doesn’t know what the type of _loop is.

function WorldService:Start()
	self._loop:scheduleSystems(self._systems)
	self._loop:begin({
		default = RunService.Heartbeat
	})
end

I could replace “self” with “WorldService”, but I’d rather prefer not to.

Can you show what your WorldService type looks like?

Here is the full code

local RunService = game:GetService("RunService")
local Matter = require(game:GetService("ReplicatedStorage"):WaitForChild("Shared"):WaitForChild("Modules"):WaitForChild("Matter"))

type WorldService = {
	world: Matter.World,
	_loop: Matter.Loop,
	_systems: {{}}
}

local WorldService = {} :: WorldService

function WorldService:Start()
	self.world = Matter.World.new()
	self._loop = Matter.Loop.new(self.world)
	self._systems = {}
	for _, child in ipairs(script.Systems:GetChildren()) do
		if child:IsA("ModuleScript") then
			table.insert(self._systems, require(child))
		end
	end
	
	self._loop:scheduleSystems(self._systems)
	self._loop:begin({
		default = RunService.Heartbeat
	})
end

return WorldService

So since functions are technically just keys within the table, you need to also include those functions in the WorldService type. For example:

type WorldService = {
    world: Matter.World,
    _loop: Matter.Loop,
    _systems: {{}},
    
    -- function types
    Start: (WorldService) -> ()
}

Note that since Start is a colon function and not a dot function, you need to include WorldService as the first parameter type. This should let the intellisense know that “self” refers to a WorldService type.

Edit: In case you’re interested in learning more about the type check system, here’s a solid resource that covers mostly everything you’d want to know: Type checking - Luau

1 Like

Correct me if i’m wrong, but I think the best way to do this is to just have a literal local variable.

local variable = 1

local function GetVariable()
   return variable
end

There isn’t really a point in using self in a script like this. In fact, it’s just fancy syntax sugar. It’s actually detrimental because it does not support easily support intellisense. That, or Roblox needs to improve the autocompletion to not be a constant pain

1 Like

Yea, that’s definitely a solution to keeping internal variables private and exposing exactly what you want, but wasn’t this what you wanted to avoid initially?

Also, I’m curious to know what you were having trouble with getting intellisense support to work. I agree it can be a bit of a headache, but I haven’t really ran into a situation yet where I absolutely couldn’t get the type system to work the way I wanted.

1 Like

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