Skip to content
AgileLogics
5 min read

Modules and require

Prerequisites: Object-oriented programming

A module is a file that returns a table of functions. require loads it.

-- illustrative
-- mathutils.lua
local M = {}

local function check(n)            -- private: not in M
  assert(type(n) == "number", "expected a number")
end

function M.square(n)
  check(n)
  return n * n
end

function M.clamp(n, low, high)
  return math.max(low, math.min(high, n))
end

return M
-- illustrative
-- main.lua
local mathutils = require("mathutils")
print(mathutils.square(7))
print(mathutils.clamp(150, 0, 100))

Here are both files in one runnable example (the first lines write mathutils.lua to disk):

local f = assert(io.open("mathutils.lua", "w"))
f:write([[
local M = {}
local loads = 0
loads = loads + 1
print("loading mathutils")
function M.square(n) return n * n end
function M.clamp(n, low, high) return math.max(low, math.min(high, n)) end
return M
]])
f:close()

local mathutils = require("mathutils")
print(mathutils.square(7))
print(mathutils.clamp(150, 0, 100))
local again = require("mathutils")
print(again == mathutils)
loading mathutils
49
100
true
  • require("mathutils") finds mathutils.lua, runs it once, and caches the returned table in package.loaded. Later require calls return the same table — "loading mathutils" printed only once.
  • Locals in the module file (like check) are private.
  • Assign the result to a local — modules shouldn't create globals.

Where require looks

require("a.b") replaces dots with directory separators and tries each template in package.path:

for template in package.path:gmatch("[^;]+") do
  if template:find("^%./") or template:find("^%.\\") then print(template) end
end
.\?.lua
.\?\init.lua

(This output is from Windows; on Linux and macOS the templates are ./?.lua and ./?/init.lua.) So require("utils.strings") loads utils/strings.lua or utils/strings/init.lua from the current folder, among other places. C libraries are found through package.cpath. Hosts like Neovim and LÖVE add their own folders.

Module structure tips

  • One module per file, returning a table (or a class).
  • Keep module-level state minimal — it's shared by everything that requires the module.
  • To reload a module during development, set package.loaded["name"] = nil and require it again.