Every real Lua project and every host (Neovim, LÖVE) organises code as modules.
A module is just a file that returns a value, almost always a table of functions. Keep the implementation local and export only what you put in the table.
require("mylib.util") turns the dots into directory separators and searches the templates in package.path (for Lua files, e.g. ./?.lua;./?/init.lua;...) and package.cpath (for C libraries). The loaded result is cached in package.loaded, so the file runs once no matter how many times it is required, and every caller shares the same table.
Conventions:
- Return a table; don't set globals from a module.
- Name the local table
M(or the module name) andreturn Mat the end. requirepasses the module name as...to the chunk, useful for requiring siblings.
To reload during development set package.loaded[name] = nil and require again. Hosts usually add their own search paths: Neovim looks under lua/ in each runtime directory, and LuaRocks, Lua's package manager, installs libraries into a tree that it adds to the paths.
Lower level, load(str) compiles a string into a function and dofile/loadfile do the same for files without caching.
Example
-- Simulate a file mathx.lua via package.preload
package.preload["mathx"] = function(name)
local M = {}
local function check(n) assert(type(n) == "number", "number expected") end
function M.clamp(x, lo, hi)
check(x)
return math.max(lo, math.min(hi, x))
end
print("loading " .. name)
return M
end
local mx = require("mathx") --> loading mathx
local again = require("mathx") -- cached: prints nothing
print(mx == again) --> true
print(mx.clamp(15, 0, 10)) --> 10
print(mx.check) --> nil (private)
print(package.path ~= nil) --> true
local f = load("return 2 + 3")
print(f()) --> 5Remember
- A module is a file that returns a table
requireruns a module once and caches it inpackage.loaded- Keep helpers
local; export only through the returned table
Watch out for
- Setting globals from a module, which leaks names into every other module.