Backwards Inheritance Module

What is it?

I made this for fun when messing with environments and I think it’ll be useful in special use cases. Imagine you have the following:

image

What this controller script does is simply require() and initialize (maybe by calling an initalize method) the child modules. The issue is these modules have no way of communicating backwards with variables that the controller has. A current and simple solution for this is simply to pass in variables into the aforementioned initialize method you want the module to have access to. This is dirty especially if you have lots of variables and it can quickly get messy.

Examples

I made a simple solution to this that simply alters the environment of the modules to inherit global variables from their caller. An example script:

x = "x!"; -- Notice how x is global
m = require(script.TestModule); -- Same with m
require(script.BackwardsInheritance).Inherit(); -- Inherit global variables (x in this case)

print(m.x); --> x!
m.DoStuff(); --> newX!

The TestModule:

test = {}; -- Notice how the module's table is global

test.BackwardsInherit = true; -- Enable backwards inheritance

function test.DoStuff()
    test.x = "newX";
    print(test.x); --> newX!
end

return test;

There’s also a BackwardsCommit property that can be set on modules (set it the same as the BackwardsInherit property). When a module alters an inherited variable it will then also alter the variable in the environment from which it was called.

Download

BackwardsInheritance Module Source
return {
	Inherit = function()
		for index, value in pairs(getfenv(2)) do
			if typeof(value) == "table" then
				if value.BackwardsInherit then
					if value.BackwardsCommit then
						local variablesToInherit = {};

						for index2, value2 in pairs(getfenv(2)) do
							if index2 ~= "script" and value2 ~= value then
								variablesToInherit[index2] = value2;
							end
						end

						getfenv(2)[index] = setmetatable(getfenv(2)[index], {
							__index = function(_, k)
								return variablesToInherit[k];
							end,
							__newindex = function(_, k, v)
								if not variablesToInherit[k] then return end
								if getfenv(2)[index] then
									getfenv(2)[index][k] = v;
									getfenv(2)[k] = v;
									variablesToInherit[k] = v;
								elseif getfenv(3)[index] then
									getfenv(3)[index][k] = v;
									getfenv(3)[k] = v;
									variablesToInherit[k] = v;
								end
							end,
						});

						continue;
					end

					for index2, value2 in pairs(getfenv(2)) do
						if index2 ~= "script" and value2 ~= value then
							getfenv(2)[index][index2] = value2;
						end
					end
				end
			end
		end
	end
}
BackwardsInheritance Module Download

BackwardsInheritance.lua (1.2 KB)

3 Likes

You can also create a table then pass the table as an argument to the module:

local module = {}
function module.Increase(Table)
    Table.X += 1
    Table.Y += 1
end
return module
local Table = {X = 0, Y = 1}

print(Table.X, Table.Y) -- 0, 1

local module = require(script.ModuleScript) -- This is the module with the code above
module.Increase(Table)

print(Table.X, Table.Y) -- 1, 2
2 Likes

Yep the actual point of this module is just to eliminate having to pass in variables and having them pre-defined for the module to access. Your example using my module can be turned into:

local module = {};

module.BackwardsInherit = true;
module.BackardsCommit = true;

function module.Increase()
    module.Table.X += 1;
    module.Table.Y += 1;
end

return module;
Table = {X = 0, Y = 1};

print(Table.X, Table.Y) --> 0, 1

mod = require(script.ModuleScript);
require(script.BackwardsInheritance).Inherit();

mod.Increase();

print(Table.X, Table.Y); --> 1, 2
1 Like

To be honest despite this being a nice way of eliminating variable creation. It’s kind of overkill for alot of most things.

If I ever need something like this I will use it… But I feel like for 99% of occasions I will probably just do something similar to @VegetationBush insinuated it’s alot neater.

It would just be difficult to figure out what is doing what and where it’s coming from. It eliminates lots of confusion.

1 Like

Yeah I fully agree with you. Like I had said I made this primarily for fun when just messing around with environments. It came to me while working on this plugin where it’s structured like so:
image

The RoUI2 controller script has over 700 lines of code that are primarily function callbacks. It’s extremely messy and I could have simply included all these callbacks in their respective modules if they had access to the variables the controller had such as the UI elements. So yeah I believe for larger projects it has significantly more use than for more basic stuff.

1 Like