I’m currently simplifying/rewriting my giant module script. In the explorer tree, my smaller new modules are parented to my original giant module. By definition, I’ll be using functions from the smaller new modules, in the original giant module. Hopefully there isn’t any confusion so far.
The problem I’m running into is: there are some functions (from the giant module) which appear often in my new smaller modules. One function in particular appears in nearly every smaller new module.
Currently, those functions are underlined orange, because they’re undefined. Makes sense. If I try to define them, I get a Cyclic module dependency error, and a Module experienced error while loading message in the output.
TL;DR: Is it a good idea to pass a function in a function? Example below:
Calling the function: littleModule.littleModuleFunction(module.bigModuleFunction) ← is this acceptable? Or disastrous practice?
Passing functions as arguments in this way is entirely acceptable and a widely used approach, particularly to prevent circular dependencies. In your situation, it provides a clean solution. This method scales effectively and prevents issues like undefined functions or cyclic errors.
This is usually referred to as dependency injection, and it is a very good practice, but still use it only when you have to. Don’t go out of your way to do it all the time.
This is something I commonly do, but not in the context of cyclic module dependency. Something I like to do with my modules is, when I have a parameter than kind of has an endless amount of possibilities (for example, with a projectile module, the path of the projectile could be a simple quadratic, or it could be bubbles floating up and down, or a magic spell moving erratically). Instead of adding all these as different options in the module, I prefer to have an argument, that is a function, that describes the path of the projectile. So if I want a new path, I can just write a new path function. For ease of use (because in the case of a projectile module, path functions aren’t all that simple), I’ve put the path functions in separate module scripts. So the script using the projectile module gets the path function from those modules, and passes it to the projectile module
This approach alleviates one of the downsides of abstraction, which is the loss of granularity. By having a function as a parameter, I am not limited in the path I want my projectile to take. It does add more complexity though
It is very acceptable. I love callbacks and use em all the time when I need to do something after something has happened. It’s used heavily for the combat in my game.
The way tables work is that they are stored in variables as pointers and not the actual value, so whatever you do to the table that its passed in the function will affect the dictionary automatically:
Lets say you have a module (LittleModule) like so:
local LittleModule = {}
function LittleModule.foo(Dictionary)
Dictionary["Hello"] = true
end
return LittleModule
Then in your other module you do this:
local LittleModule = require(path.to.module)
local MyModule = {}
print(MyModule)
-- Output: {}
-- This should add the field "Hello" with a value of 'true'
LittleModule.foo(MyModule)
print(MyModule)
-- Output: {
-- ["Hello"] = true
-- }
return MyModule