Is it possible to make it so changes to a variable will affect the original source of the variable from a module?

So, say I have a module script that contains some variables, and some of them are variables within sub-groups within groups, like so:

local module = {}
module.ship = {
   laser = {
      speed = 50,
      damage = 20
   }
}
return module

Then, from another script, when I access that variables from that module, and I create variables for those variables so that I don’t have to type the full path, like so:

settings = require(workspace.Settings)
laserSpeed = settings.ship.laser.speed
laserDamage = settings.ship.laser.damage

Then, say a circumstance happens that I want to change those values, the only way to change the actual values the module holds is to type out the full path, “settings.ship.laser.speed = 100”, because laserSpeed is a variable of its own that was set to the value which “settings.ship.laser.speed” contained at the time it was created. The same thing will be true when creating variables based off of properties of objects in the game, or even when creating a variable of another regular variable.

Is there any way in Lua to create a variable “reference”, that would reference the same location in memory of the original module variable or object property, so that i could directly manipulate the module variable without having to type the full path?

1 Like

No. I had the same sort of problem as you awhile ago and had a look around the Wiki and here but couldn’t find anything.

The best you can do is reference the table the variable is stored in.

Strings, tables, and other gc objects get passed by pointer, but variables with normal values are passed by copy.

3 Likes

With Modules everything just gets replicated over to where you require it from. There’s none of that fancy memory allocation stuff you can do with other languages like C#.

This isn’t the case, either. A module that returns a table will return the pointer to the same table on every time it’s required, because of how Roblox caches it. This, in turn, makes the table possible to edit at runtime.

Well perhaps I worded that poorly. Likewise, requiring from a module doesn’t give you the actual information stored in that module, it just replicates it over in a new table. Data modified from a required module doesn’t actually update within the module?

It does. If you require a module that returns a table it should return that same table every time without any copy mechanics; that’s how it’s worked at least.

4 Likes

I’ve done some C# and C++, and a few other languages in the past and vaguely remember being able to do this sort of thing with variable references in at least one of them. Sometimes I like Lua for its simplicity, but other times I hate it because it means you have to go a longer more tedious route to achieve the same thing. One of the shorthands that I love that I really wish existed in Lua is those simple ones with adding, subtracting, division, and multiplication, like number++ instead of number = number + 1 or number -= 5 instead of number = number - 5, etc.

1 Like

It works perfectly fine and can be used for cross-script communication for example:

--module script
local module = {count = 0}
return module

--Script1
local counter = require(workspace.ModuleScript)
counter.count = counter.count + 1
print("I counted to ".. counter.count) 
--will print "I counted to 1"

--Script2
wait()
local counter = require(workspace.ModuleScript)
counter.count = counter.count + 1
print("I counted to ".. counter.count)
--will print "I counted to 2"

If you want to be fancy you can also do something like this:

--module script
local module = {}
return module

--Script1
local module = require(workspace.ModuleScript)
local name = "Bob"

function giveName()
	return name
end

module.giveMeYourName = giveName

--Script2
local module = require(workspace.ModuleScript)
local name = "John"
wait() 
local otherGuysName = module.giveMeYourName()
print("Hi "..otherGuysName..", my name is "..name)
--will print "Hi Bob, my name is John"
3 Likes

Here is a pointer implementation. As long as all variables use this pointer, they will all refer to the same value. Since indexing something with nil usually throws an error, I’ve taken over this functionality for pointer de-referencing. Enjoy!

Proxy Module
local data = setmetatable({}, {__mode = 'k'})

local proxy = {
	__metatable = 'Proxy metatables are locked';
}
function proxy:__index(key)
	if key == nil then
		-- dereference
		return data[self]
	else
		return data[self][key]
	end
end
function proxy:__newindex(key, value)
	if key == nil then
		data[self] = value
	else
		data[self][key] = value
	end
end
function proxy:__call(...)
	return data[self](...)
end
function proxy:__concat(value)
	return data[self] .. value
end
function proxy:__add(value)
	return data[self] + value
end
function proxy:__sub(value)
	return data[self] - value
end
function proxy:__mul(value)
	return data[self] * value
end
function proxy:__div(value)
	return data[self] / value
end
function proxy:__mod(value)
	return data[self] % value
end
function proxy:__pow(value)
	return data[self] ^ value
end
function proxy:__tostring(value)
	return '*' .. tostring(data[self])
end
function proxy:__eq(value)
	return data[self] == value
end
function proxy:__lt(value)
	return data[self] < value
end
function proxy:__le(value)
	return data[self] <= value
end
function proxy:__len()
	return #data[self]
end

local function newProxy(value)
	local new = newproxy(true)

	data[new] = value

	local metatable = getmetatable(new)
	for key, value in next, proxy do
		metatable[key] = value
	end

	return new
end

return newProxy
Example Usage
local Proxy = require(script.Parent.Proxy)

local function mutate(num)
	-- dereference and change
	num[nil] = num + 1
end

local ptr = Proxy(5)
print(ptr) --> "*5"
mutate(ptr)
print(ptr) --> "*6"

(note that rawset, rawget, getmetatable, setmetatable and type work differently on pointers. You’ll need to de-reference the pointer before calling these functions. Natural operations like + and - don’t make sense on Lua pointers, to I made it act more like a reference in these cases.)

4 Likes

There’s a simple pattern you can use to get behavior like this - getters and setters.

local function getLaserSpeed()
     return settings.ship.laser.speed
end

local function setLaserSpeed(value)
    settings.ship.laser.speed = value
end

setLaserSpeed(getLaserSpeed() + 1)

Doesn’t require any metatables, straightforward to read/understand/maintain/debug. They’re very common in many languages, I use them both in C++ and Lua daily.

One nice thing about getters/setters is that the actual source of the value can come from anywhere - not just a variable containing it. You can also put code in the setter that does special things whenever the value is changed.

7 Likes

Yeah I’ve actually done a lot of those in other languages. Didn’t really think about that for lua. That’s not bad actually. It’s a few extra lines of code to write for each variable but if you’re going to be getting/setting the variable many times in many places than it’s probably worth it.