How should I access other modules within a module? - module

I'm struggling with the "global" aspect of functions as it relates to modules. Maybe someone here could tell me if this example would work, explain why and then tell me the right way to do it.
If I have two modules:
f1.lua
local mod = T{}
function mod.print_msg(msg)
print(msg)
end
return mod
f2.lua
local mod = T{}
function mod.print_hello()
msgmod.print_msg('Hello')
end
return mod
and both are called in a "main" file
msgmod = assert(loadfile(file_path .. 'f1.lua'))()
himod = assert(loadfile(file_path .. 'f2.lua'))()
himod.print_hello()
Would print_hello still work if called from f2 or would I need to loadfile() f1.lua in f2?

It would work if called after the msgmod = ... has been executed (in any file), but not before. This is a confusing situation due to the usage of globals.
Typically, you do not want to use globals like this in modules. You should handle dependencies using require just as you would #include them in C++. So, f2.lua, which wants to use print_msg defined in f1.lua, might look like this:
local f1 = require('f1')
local mod = T{}
function mod.print_hello()
f1.print_msg('Hello')
end
return mod
You should also use require in your main file (and get in the habit of making everything local):
local msgmod = require('f1')
local himod = require('f2')
himod.print_hello()
Note that we could have omitted the first line, since we aren't actually using f1 in main, and f2 will require it automatically when we require f2. Unlike loadfile, require automatically caches loaded modules such that they are loaded only once. Again, require is almost always what you want to use.
The general pattern for writing modules is to require all dependency modules into locals, then use them as you like to implement your module functions:
local dep1 = require('dep1')
local dep2 = require('dep2')
...
local mod = {}
function mod.foo ()
return dep1.bar(dep2.bazz())
end
return mod

Related

unable to use imported modules in event_handler lua

I'm writting a basic event handler in lua which uses some code located in another module
require "caves"
script.on_event({defines.events.on_player_dropped_item}, function(e)
caves.init_layer(game)
player = game.players[e.player_index]
caves.move_down(player)
end
)
but whenever the event is triggered i get following error
attempt to index global 'caves' (a nil value)
why is this and how do i solve it?
You open up the module in question and see what it exports (which global variables are assigned and which locals are returned in the bottom of the file). Or pester the mod author to create interface.
Lua require(filename) only looks up a file filename.lua and runs it, which stands for module initialization. If anything is returned by running the file, it is assigned into lua's (not-so) hidden table (might as well say, a cache of the require function), if nothing is returned but there was no errors, the boolean true is assigned to that table to indicate that filename.lua has already been loaded before. The same true is returned to the variable to the left of equals in the caves = require('caves').
Anything else is left up to author's conscience.
If inside the module file functions are written like this (two variants shown):
init_layer = function(game)
%do smth
end
function move_down(player)
%do smth
end
then after call to require these functions are in your global environment, overwriting your variables with same names.
If they are like this:
local init_layer = function(game)
%do smth
end
local function move_down(player)
%do smth
end
then you won't get them from outside.
Your code expects that the module is written as:
caves = {
init_layer = function(game)
%do smth
end
}
caves.move_down=function(player)
%do smth
end
This is the old way of doing modules, it is currently moved away, but not forbidden. Many massive libraries like torch still use it because you'd end up assigning them to the same named globals anyway.
Кирилл's answer is relevant to newer style:
local caves={
%same as above
}
%same as above
return caves
We here cannot know more about this. The rest is up to you, lua scripts are de-facto open-source anyways.
Addendum: The event_handler is not part of lua language, it is something provided by your host program in which lua is embedded and the corresponding tag is redundant.
You should consult your software documentation on what script.on_event does in this particular case it is likely does not matter, but in general the function that takes another function as argument can dump it to string and then try to load it and run in the different environment without the upvalues and globals that the latter may reference.
require() does not create global table automatically, it returns module value to where you call this function. To access module via global variable, you should assign it manually:
local caves = require "caves"

How to require multiple modules in a single statement?

I want to require several Lua modules at once, similar the to the asterisk signifier from Java (import java.awt.*). This the structure I organized my modules in subdirectories:
<myapp>
-- calculations
-- calc1
-- calc2
-- calc3
-- helper
-- help1
-- help2
-- print
--graphprinter
--matrixprinter
My client requires each module of a subpath:
local graphprinter = require("myapp.helper.print.graphprinter")
local matrixprinter = require("myapp.helper.print.matrixprinter")
I would prefer an automatic multi-require, which derives the local table names from the module path and requires a whole subpath at once. This could be the format: require("myapp.helper.print.*"). Automatically the local table names should be created for each module of the subdirectory, so that there isn't any difference as I would have required them module by module.
Why don't you just write an init.lua file for each folder that requires all the other libraries?
For example, in calculations you write a file that contains
return {
calc1 = require "calc1";
calc2 = require "calc2";
calc3 = require "calc3";
}
Then you can just write calculations = require "calculations" and it will automatically load calculations.calc<1-3>
This can be done for the entire directory structure, and require "helper" can call require "help1" which in turn calls require "print" and in the end you can find your functions in helper.help1.print.<function>
Short explanation of how this works: When you run require "library" lua will either try to include a file called library.lua or the file init.lua located in a library directory. This is also the reason why you do require "dir.lib" instead of require "dir/lib"; because, if done right, when you just require "dir" it will return a table that contains the field lib, so you would access it as dir.lib.<function>.
The module env partially achieves what you are looking for, though it is far from perfect.
It allows for grouped / named imports, with some caveats - the main one being you must manually manage your environments. In addition, you'll need to write index files (default init.lua, unless you write a custom path set), since it is intended for use with modules that export tables.
Here's a bit of an example. First we need to properly set up our file structure.
-- main.lua
-- calculations /
-- calc1.lua
-- calc2.lua
-- calc3.lua
-- init.lua
-- helper /
-- print /
-- init.lua
-- graphprinter.lua
-- matrixprinter.lua
The index files, which are slightly tedious:
-- calculations/init
return {
calc1 = require 'calculations.calc1',
calc2 = require 'calculations.calc2',
calc3 = require 'calculations.calc3'
}
and
-- helpers/print/init
return {
graphprinter = require 'helper.print.graphprinter',
matrixprinter = require 'helper.print.matrixprinter'
}
Inside your main file. The major caveat shows itself quickly, you must use the function returned by requiring 'env' to override your local environment. Passing no arguments will create a clone of your current environment (preserving require, etc.).
-- main.lua
local _ENV = require 'env' () -- (see notes below)
The new environment will be given an import function, which takes a single argument, a string represent the path or module name to import into the current environment. The return value is a transient table that can be used to further alter the environment state.
import 'helper/print' :use '*'
import 'calculations' :use '*'
One of the functions on the transient table is :use, which either takes a table indicating which values to pull from the required table, or the string '*', which indicates you want all values from the required table placed in your current environment
print(matrixprinter, graphprinter) --> function: 0x{...} function: 0x{...} (or so)
The final caveat is that all the paths you've seen are reliant on the cwd being the same as the one that holds main.lua. lua myapp/main.lua will fail loudly, unless you place your sub modules in a static location, and adjust package.path / import.paths correctly.
Seems like a lot of work to avoid a couple of lines of require statements.
Disclaimer: I wrote env as a bit of an experiment.
Note that import does not currently support the . syntax (you need to use your OS path delimiter), or proper exploding of tables into table chains. I have a bit of a patch in the works that addresses this.
Lua 5.2+ uses _ENV to override local environments. For Lua 5.1, you'll need to use setfenv.
As mentioned above, Lua has no real notion of directories. To really do what you want (with less overhead), you'll need to write your own custom module loader, environment handler, and likely make use of a module like LuaFileSystem to reliably 'automatically' load all files in a directory.
TL;DR:
This is a tricky topic.
There's nothing built into the language to make this easy.
You'll need to write something custom.
There will always be drawbacks.

Lua - How do I dynamically call a module?

Here's some much-simiplified Lua code I'm working with. I need to know how to dynamically call another module ('zebra'):
avar = require "avar"
bvar = require "bvar"
function create(zebra)
print(zebra.new())
end
print(create(avar))
And here are two modules:
local Avar = {}
function Avar.new()
return "avar"
end
return Avar
local Bvar = {}
function Bvar.new()
return "new"
end
function Bvar.old()
return "old"
end
return Bvar
If I try to pass in the string "avar" to my 'create' function, it doesn't work. If I pass in the word 'avar' with no quotes, it does work, however, I don't understand what avar with no quotes is? It seems to be a blank table? Not sure how to pass a blank table as an argument in my main program.
But maybe I'm totally on the wrong path. How do I dynamically call modules?
You can require any time:
function create(zebraModuleName)
zebraType = require(zebraModuleName)
print(zebraType .new())
end
print(create("avar"))
print(create("bvar"))
avar without the quotes is a global variable you created. It is initialized to the value returned by the require function1, which is the value returned by the module you are invoking. In this case, its a table with the new field that happens to be a function.
1 Importing a modules in Lua is done via regular functions instead of a special syntax. The function call parenthesis can be ommited because parens are optional if you write a function call with a single argument and that argument is a string or a table.
Other than that, there are also some other things you are confusing here:
The table you are storing on avar is not empty! You can print its contents by doing for k,v in pairs(avar) do print(k,v) end to see that.
The avar, bvar and create variables are global by default and will be seen by other modules. Most of the time you would rather make them local instead.
local avar = -- ...
local bvar = -- ...
local function create (zebra)
-- ...
end
The create function clearly expects a table since it does table indexing on its argument (getting the new key and calling it). The string doesn't have a "new" key so it won't work.
You aren't really dynamically calling a module. You are requiring that module in a regular way and it just happens that you pass the module return value into a function.
create always returns nil so there is no point in doing print(create(avar)). You probablu want to modify create to return its object instead of printing it.
You can use standard require from lua language or build your own loader using metatables/metamethods.
1. create a global function:
function dynrequire (module)
return setmetatable ({},{
__index = function (table,key)
return require(module..'.'..key)
end
})
end
2. Create your project tree visible to package.path
./MySwiss/
\___ init.lua
\___ cut.lua
\___ glue.lua
\___ dosomething.lua
3. Make your module dynamic
you only need to put this line on your MySwiss/init.lua (as if you were namespacing a PHP class):
return dynrequire('MySwiss')
4. Require your module and use subproperties dynamically
On your script you only need to require MySwiss, and the folder file (or subfolders with dynrequire('MySwiss.SubFolderName').
var X = require('MySwiss')
X.glue()
Note that MySwiss doesn't have glue key. But when you try access de glue key the metamethod __index try to require submodule. You can the full project tree using this technique. The only downside is the external dependencies not packed this way.

Lua - how do I use one lib from another?

I'm having trouble using one Lua lib from inside another. I'm not sure about the best way to do it.
I've got a library that returns a (non-global) table with functions, like this:
-- foo.lua
local foo = {}
function foo:m1(...) ... end
function foo:m2(...) ... end
return foo
This library can be inserted in either the global or local scope, depending on what the user wants:
-- globally
foo = require('foo')
-- or locally
local foo = require('foo')
I'm now trying to create another lib (let's call it bar) that requires/uses this foo lib. Something like this:
-- bar.lua
local bar={}
function bar:m3(...)
...
foo:m1()
...
end
My trouble is - I don't know how to "pass" foo to bar.
Ideally I'd like to send it as a parameter to require:
local foo = require('foo')
local bar = require('bar', foo)
But I don't think that's possible (is it?). The other option I could think about was adding a init method to bar:
local foo = require('foo')
local bar = require('bar')
bar:init(foo)
This works, but doesn't look very clean to me; it's possible to forget adding that third line, leaving bar in an "unsafe" state.
Is there a common Lua idiom/method that I'm missing?
Just call require 'foo' directly in you bar module. This is perfectly legal. The foo module will be loaded only once. In order not to leak it out of the bar module, store it in a local variable.
You can use this idiom also to separate one big module into several parts, and have one module require all the others. The user will have to require only one module.

Lua callback into module

My script registers itself for a callback using
require "cmodule"
index = 1
cmodule.RegisterSoftButtonDownCallback(index, "callbackFunc")
where callbackFunc is the name (a string) of the callback function. Now I turned this script into a module, but the callback is not called anymore, I assume because the callback function is not in the scope of the cmodule. How can I solve this? (Lua newbie)
cmodule is a device driver that has Lua bindings.
Edit: My complete solution based in the answer from BMitch below:
require "cmodule"
local modname = "myModule"
local M = {}
_G[modname] = M
package.loaded[modname] = M
local cmodule = cmodule
local _G = _G
setfenv(1,M)
function callbackFunc()
-- use module vars here
end
_G["myModule_callbackFunc"] = callbackFunc
index = 1
cmodule.RegisterSoftButtonDownCallback(index, "myModule_callbackFunc")
You need to have something defined in the global space for a string to be evaluated back to a function call.
Depending on how they implemented RegisterSoftButtonDownCallback, you may be stuck defining the function itself, rather than the table/field combination like myModule.callbackFunc. To minimize the namespace pollution, if you can't use myModule.callbackFunc, then I'd suggest myModule_callbackFunc=myModule.callbackFunc or something similar. So your code would look like:
require "cmodule"
index = 1
myModule_callbackFunc=myModule.callbackFunc
cmodule.RegisterSoftButtonDownCallback(index, "myModule_callbackFunc")
For a better fix, I would work with the cmodule developers to get their program to accept a function pointer rather than a string. Then your code would look like:
require "cmodule"
index = 1
cmodule.RegisterSoftButtonDownCallback(index, myModule.callbackFunc)