Pseudo globals trick or nightmare?

I’m trying a file require/linkage structure to simulate globals while still keeping everything local. 

Basically, I’m storing references to information from many modules inside a settings module.

For example, module A requires the settings module and then inserts information inside a table in that module - sort of a reverse require method?

[lua]

local moduleA = {}

local function test () print(“test”) end ; moduleA.test = test

local settings = require(‘settings.lua’)

settings.table.moduleA = moduleA – add the module

settings.table.test = moduleA.test  – add a function

[/lua]

I could then have module B require the settings module and then access the settings.table to retrieve items stored there by module A.

Module A could also retrieve it’s own information from settings.table

I’m treating settings.table as  repository for all the other modules to get information (values, methods, tables ) from each other without direct requires to one another.

So far, it seems almost fine.  When I do a globals check, nothing is triggered as global and I’ve avoided circular require reference errors.  However, I have some memory leaks that I’m starting to think this structure is responsible for.

Maybe one of you Lua gurus can point out the pitfalls or flaws of trying this method.  I’ve attached an image to try to illustrate what I’m doing.

However, I have some memory leaks that I’m starting to think this structure is responsible for.

In your example above, test is stored as both settings.table.test and module.test. If you nil / overwrite one of these, the function remains safe from garbage collection so long as you leave the other alone (similarly if you evict the tables themselves). If this is typical it might very well account for your situation.

Also, with regard to “simulat[ing] globals while still keeping everything local”, you might like setfenv (think config.lua ). This was the direction I thought you were headed, during the first few paragraphs.  :slight_smile:

Thanks @StarCrunch - I can always count on you to understand the nuances and details.  I’m going to play around with setfenv when I get a chance.  It seems that the memory leak is coming from some timers not the file structure but I wanted to run this by everyone in case there is something unstable I’m doing - something that would bite me in butt months down the road  :slight_smile:

it would probably be cleaner to flip “who-maintains-who”, let settings maintain itself, fe:

-- settings.lua return { modA = require("moduleA"), modB = require("moduleB"), modC = require("moduleC") } -- somewhereelse.lua local settings = require("settings") settings.modA.test()

[EDIT: oh nevermind, (though I still think this is cleaner), your problem is circular-requires, right? i missed that on initial read]

I am not sure if you want to do this as an exercise, but I just want to point out that I do have a plugin that does this.

GBC Data Cabinet allows you to manage temporary and persistent data that can be used throughout your game/app.  The stored data is not stored as global.

–john

wrt circular requires: in general, lua will attempt to prevent this sort of thing:

-- moduleA local modB = require("moduleB") return { test = function() print(modB.test()) end } -- moduleB local modA = require("moduleA") return { test = function() print(modA.test()) end }  

…now, there are some metatable tricks you might be able to use to defer loading that might get around that, but otherwise lua will still spot the loop even if you attempt to obscure it with a common parent loader (aka your “settings”)… what it usually means is that you need a code restructure - ie, pulling out non-dependent code into a separate “moduleC” that both A and B could require without creating a loop.  if you could post specifics for two modules needing to require each other maybe someone could suggest a restructuring.

Also be careful using a table named “table”. You didn’t prefix it with “local” in your settings.lua example and if that accidentally gets made global, it will trash the table.* APIs.

Rob

@davidbollinger

I agree with your first post.  It is cleaner to do the requires inside the settings table except for the modules that need to require the settings table.  It looks prettier too!

metatables and coroutines are top on my list for things I need to understand better, so far I’ve been able to accomplish everything i need to without mastering metatables but that day is coming.

@schizoid2k

I’m a marketplace junky and have all three of your plugins.  I was playing around with the Object Pool for the another project.  I’ll have to look at the Data Cabinet again, Thanks!

@Rob Miracle

Sorry, I should have said “myTable” to show that “table” just meant a table instead of the literal syntax  :wink:

I appreciate all of your responses.  As a self-taught programmer I’m always fearful of getting into trouble from the unknown unknowns that programmers with a more structured pedigree might call common sense.  It sounds like I’m not doing anything that will loop around a give me a kick later (at least not anything I can’t fix).

I may be misunderstanding what you want but…

I use a “global” lua file that contains many many functions and storage mediums.  I locally require it in every other lua file that depends on those functions, etc.  

Seen as consequential requires (of the “global” lua file) will receive a pointer to the first instance there are no multiple instancing issues and your global functions are now local to the calling lua file.

Personally, when I see A requires B which also exposes C, D and E… it just screams refactor to me.  

I believe OP essentially has A which requires B which requires A.  further, apparently, all such bi-directional permutations of A, B, and C. perhaps even 3-step loops:  A requires B requires C requires A (inferred from questions, rather than statements).  perhaps with even more planned, D, E, F…

it isn’t entirely clear what problem this solution is trying to cause.

 
without more concrete code samples to suggest a better restructuring, it almost sounds like your “settings” file is supposed to serve as what some might call a “context” (or maybe an “engine”, or “environment”, terms may vary).  that is, a common meeting point for all your disparate modules to accumulate.
 
like maybe you have an audio support module, a network support module, some display support modules, file, io, math, utility, etc…   and you’re simply trying to set up a one-require-for-everything sort of thing?  does that sound about right?
 
if so, you could potentially get around the circular requires by have an “init” method on your context that passes a reference of the context itself to each of the sub-modules.  sub-modules could then communicate to each other by first traversing up to the context, then back down to another sub-module.  i think (?) that’s essentially what you’re proposing.
 
if so, then consider something like this (pseudo-code only):

-- context.lua return { modA = require("moduleA"), modB = require("moduleB"), -- etc init = function(self) self.modA:init(self) self.modB:init(self) end } -- moduleA return { init = function(self, context) -- store the reference to our "parent" self.context = context end, test = function(self) -- this is how one module would reference another self.context.modB.doSomething() end } -- moduleB would look similar to moduleA -- main.lua local context = require("context") -- all sub-modules \*loaded\* context:init() -- all sub-modules \*inited\* context.modA:test() -- anyothermodulethatneedscontext.lua local context = require("context") -- everything is already loaded AND inited, this is just a reference to it context.modA:test()

 
still a lot of interdependencies, but perhaps a cleaner way of setting them up (not resolving them)
hth

and you’re simply trying to set up a one-require-for-everything sort of thing?  does that sound about right?

@davebollinger  Yes, I’m looking for a one-require-for-everything sort of thing.  The project I’m working on now has about 40 modules and 8,000 lines of code so my require web was getting excessively hard to manage as I came up with new ideas and added more modules on the fly.  The size makes it hard to show all the permutations of how I’m using the settings file / “require hub” but it seems to be a stable method.  I have an “init” method but it’s much clunkier than your sample so I’ll play around with making it more streamlined like your example.  Thanks!

@SGS I like the term “require hub” that I used above.  That allows me to store all the functional pieces of code I need in a local file so i can control their exposure without getting caught in a web of requires.  This might seem needlessly complicated but I’m realizing now that it is a very visual method and I think that’s why it appeals to me as a highly visual person.

Ah now I get what you mean… personally, I don’t like this approach.  I am very much of the KISS mindset and like to keep things discrete and loosely-coupled.  In my latest project and I have over 60 modules (20-odd are UI and the rest are animation, helpers and effects modules).

I personally much prefer the module.function() approach to a framework.context.modules.modA.function() version.  It just seems much cleaner to me, more readable and generally faster.

Sublime’s intellisense works on **module.function() **and being able to right click drill down into function() is a big boost to productivity - not sure these niceties would work the Dave’s proposed schema?

Just offering my opinion and you should choose what works best for you.

@sporkfin - note that what i posted was just an alternate way of doing what you already appear to be doing, i neither endorse nor disapprove of it, take your pick.
 

 
what tends to happen is actually quite “silly”…
somewhere deep in moduleA, for example, you’ll end up needing repeated access to moduleB, so you alias it:

function moduleA:doSomething() local modB = self.app.framework.context.modules.modB modB.doStuff() modB.doOtherStuff() modB.etcetera() end

 
so in the end you’re no better off than if you’d just written:
 

local modB = require("moduleB")

 
at module scope.  But there are still a lot of folks that like the “framework” approach.

let me suggest one further heresy…  (again, this is just food for thought, without endorsing/disapproving of it)
 
let’s say you take your “framework” (aka “settings”) and decide to refer to it as “_F”.  let’s further say that you eventually find that nearly every module everywhere ends up requiring it.  that is, every module has a local “_F” defined in its environment.

-- everymodule.lua local \_F = require("framework")

so is there really any benefit to making “_F” local rather than global?  remember, it’s YOUR environment to do with as you wish, and the name is already in use everywhere, so it’s not like you’d risk “clobbering” some other mystery global from an unknown source.
 
it turns out that there is in fact a small performance benefit to a local _F over a global _F, but it would be fair to ask if that difference is of any practical consequence for your particular use.
 
in other words, let’s say you allowed yourself the use of ONE global:  _F.   Q:  Would the world come to an end???   A: probably not.
 
for example:

-- main.lua \_F = { modA = require("moduleA"), modB = require("moduleB"), --etc } -- moduleA function moduleA.test() \_F.modB.doSomething() end

Avoiding the careless use of globals is a Good Thing, and it is entirely possible (and not very difficult) to avoid them entirely, if you wish.  But, religious zealotry aside, there really isn’t anything wrong with the considered use of globals.

[edit: code formatting got clobbered during post]

@dave you realise you will could get lynched for simply promoting the use of… (says quietly) global functions.  Even though most of the API is globally-scoped!  But then again I get lynched for still using storyboard :wink:

I understand the official stance of “globals are bad” as it can trip up a lot of new devs… but when used correctly they can be a positive.

For anything remotely CPU-bound local is a must.

for sure!  but I’m tough (and occasionally ornery to boot), so let them bring it on.  :smiley:

back to the OP, the advantage of having the “framework” in a table somewhere (don’t care if global or local, don’t care how implemented, don’t care etc) is that the table-lookup to resolve something like “framework.modB” from within “moduleA.test()” for example will be deferred until execution-time, thus allowing circular references, versus a module-scoped require which has to resolve at load-time (the source of OP’s circular require problem in the first place).

so, in general, OP seems to be on the right track, given that a deeper restructure/refactor to simplify and remove the interdependencies (thus allowing module-scope non-circular requires) seems to be ruled out a priori.

downside is that it will incur a slight additional performance penalty for that table-lookup - but that’s the trade-off.

If you know what you’re doing and you know why you’re doing it, globals can be an effective tool. There is a reason why they are allowed in the language.

The problem is too many developers, in particular, new developers or those new to Lua don’t have a grasp on scope and issues created with not understanding Lua’s one-pass nature. Globals are the “easy” way out around these problems. Which of course, “easy” leads to very hard to track down bugs, memory leaks and hours of frustration.

As developers become more advanced and understand these issues and start writing more complex apps, performance kicks in and global’s can really hurt you in some cases.

If it makes more sense to set up a framework with well name-spaced API’s that are not going to conflict with other’s libraries, you can do so in global space and always re-localize particular API calls when performance is an issue. 

Globals are a tool in your toolbox. Most of the time all you need is a screwdriver or a hammer or duct tape, but there are times when a chainsaw is the right tool to use.

Rob

However, I have some memory leaks that I’m starting to think this structure is responsible for.

In your example above, test is stored as both settings.table.test and module.test. If you nil / overwrite one of these, the function remains safe from garbage collection so long as you leave the other alone (similarly if you evict the tables themselves). If this is typical it might very well account for your situation.

Also, with regard to “simulat[ing] globals while still keeping everything local”, you might like setfenv (think config.lua ). This was the direction I thought you were headed, during the first few paragraphs.  :slight_smile:

Thanks @StarCrunch - I can always count on you to understand the nuances and details.  I’m going to play around with setfenv when I get a chance.  It seems that the memory leak is coming from some timers not the file structure but I wanted to run this by everyone in case there is something unstable I’m doing - something that would bite me in butt months down the road  :slight_smile: