Modules and Packages

21.1 Writing a Module

-- mathx.lua
local M = {}                          -- module table

local function helper(x)             -- private (local to the file)
    return x * 2
end

function M.double(x) return helper(x) end
function M.clamp(x, lo, hi) return math.max(lo, math.min(hi, x)) end
M.VERSION = "1.0.0"

return M
-- main.lua
local mathx = require("mathx")       -- searches package.path; loads the file once
print(mathx.double(21), mathx.clamp(15, 0, 10), mathx.VERSION)
local same = require("mathx")        -- cached in package.loaded["mathx"]
print(same == mathx)                 --> true

21.2 How require Works

Environment variables: LUA_PATH, LUA_CPATH (and versioned LUA_PATH_5_4). ;; in them means "the default path".

require passes the module name (and, in 5.4, the loader data such as the file path) to the chunk: local modname, path = ....

print(package.path)       -- e.g., "./?.lua;./?/init.lua;/usr/local/share/lua/5.4/?.lua;..."
print(package.cpath)      -- C libraries: "./?.so;/usr/local/lib/lua/5.4/?.so;..."
package.path = "./lib/?.lua;./lib/?/init.lua;" .. package.path
require("utils.strings")  -- dots become directory separators: utils/strings.lua
package.loaded["mathx"] = nil   -- force a reload on the next require
package.preload["virtual"] = function() return {hello = function() return "hi" end} end
print(require("virtual").hello())
-- Searchers (package.searchers in 5.2+, package.loaders in 5.1): preload, Lua files, C libraries, all-in-one
table.insert(package.searchers, 2, function(name) --[[ custom loader, e.g., from a zip or database ]] end)

21.3 Module Patterns

-- Package with submodules: mylib/init.lua, mylib/core.lua, mylib/util.lua
-- mylib/init.lua
local M = {}
M.core = require("mylib.core")
M.util = require("mylib.util")
return M

-- Avoid polluting globals: never write `module(...)` (deprecated 5.1 style)
-- Lazy-loaded submodules
local lazy = setmetatable({}, {__index = function(t, k) local m = require("mylib." .. k); t[k] = m; return m end})