Modules in Lua
Modules are a way to organize Lua code into reusable, maintainable units. They help you structure larger programs and share functionality between different parts of your application.
What is a Module?
A module is a Lua file that contains related functions, variables, and data that can be loaded and used in other Lua programs:
-- mymodule.lua
local mymodule = {}
function mymodule.hello(name)
return "Hello, " .. name .. "!"
end
function mymodule.add(a, b)
return a + b
end
return mymoduleCreating and Using Modules
Basic Module Creation
-- calculator.lua
local calculator = {}
-- Private variables (local to the module)
local version = "1.0"
-- Public functions
function calculator.add(a, b)
return a + b
end
function calculator.subtract(a, b)
return a - b
end
function calculator.multiply(a, b)
return a * b
end
function calculator.divide(a, b)
if b == 0 then
error("Division by zero!")
end
return a / b
end
-- Function to access private data
function calculator.get_version()
return version
end
return calculatorUsing Modules
-- main.lua
local calc = require("calculator")
local sum = calc.add(10, 5)
print("Sum:", sum) -- Sum: 15
local product = calc.multiply(4, 7)
print("Product:", product) -- Product: 28
print("Calculator version:", calc.get_version()) -- Calculator version: 1.0Module Search Path
Lua searches for modules in the paths defined by package.path:
-- View the current module search path
print(package.path)
-- Typical paths might include:
-- ./?.lua
-- /usr/share/lua/5.4/?.lua
-- /usr/local/share/lua/5.4/?.luaModule Naming Conventions
-- Different ways to require the same module
local utils = require("utils") -- utils.lua
local math_lib = require("math_lib") -- math_lib.lua
local http = require("socket.http") -- socket/http.luaAdvanced Module Patterns
Module with State
-- counter.lua
local counter = {
count = 0,
history = {}
}
function counter.increment(inc)
inc = inc or 1
counter.count = counter.count + inc
table.insert(counter.history, counter.count)
return counter.count
end
function counter.reset()
counter.count = 0
counter.history = {}
end
function counter.get_count()
return counter.count
end
function counter.get_history()
return counter.history
end
return counterModule with Configuration
-- database.lua
local database = {
config = {
host = "localhost",
port = 5432,
name = "myapp",
timeout = 30
},
connection = nil
}
function database.set_config(new_config)
for key, value in pairs(new_config) do
database.config[key] = value
end
end
function database.connect()
-- Simulated connection
database.connection = {
connected = true,
config = database.config
}
print("Connected to database:", database.config.name)
end
function database.query(sql)
if not database.connection then
database.connect()
end
print("Executing query:", sql)
return "Query result"
end
return databaseModule with Dependencies
-- user_manager.lua
local json = require("json") -- Assuming a json module exists
local utils = require("utils")
local user_manager = {
users = {}
}
function user_manager.add_user(user_data)
-- Validate using utils module
if not utils.validate_email(user_data.email) then
error("Invalid email address")
end
local user = {
id = #user_manager.users + 1,
name = user_data.name,
email = user_data.email,
created_at = os.time()
}
table.insert(user_manager.users, user)
return user
end
function user_manager.get_user(id)
for _, user in ipairs(user_manager.users) do
if user.id == id then
return user
end
end
return nil
end
function user_manager.export_to_json()
return json.encode(user_manager.users)
end
return user_managerModule Best Practices
Private vs Public Members
-- good_module.lua
local M = {} -- Public module table
-- Private helper function
local function validate_input(input)
return type(input) == "string" and #input > 0
end
-- Public function that uses private helper
function M.process_string(input)
if not validate_input(input) then
error("Invalid input")
end
return string.upper(input)
end
-- Private variable
local internal_counter = 0
-- Public function using private variable
function M.get_next_id()
internal_counter = internal_counter + 1
return internal_counter
end
return MModule Initialization
-- config.lua
local M = {}
-- Module initialization function
function M.init(user_config)
M.config = {
debug = false,
timeout = 30,
retries = 3
}
-- Merge with user config
if user_config then
for key, value in pairs(user_config) do
M.config[key] = value
end
end
end
function M.get_config(key)
return M.config[key]
end
-- Auto-initialize if not already done
local initialized = false
local function ensure_initialized()
if not initialized then
M.init()
initialized = true
end
end
function M.debug(message)
ensure_initialized()
if M.config.debug then
print("[DEBUG]", message)
end
end
return MPractical Examples
Math Utils Module
-- math_utils.lua
local M = {}
-- Calculate factorial
function M.factorial(n)
if n <= 1 then
return 1
end
return n * M.factorial(n - 1)
end
-- Generate Fibonacci sequence
function M.fibonacci(n)
if n <= 0 then return 0 end
if n == 1 then return 1 end
local a, b = 0, 1
for i = 2, n do
a, b = b, a + b
end
return b
end
-- Calculate greatest common divisor
function M.gcd(a, b)
while b ~= 0 do
a, b = b, a % b
end
return a
end
-- Check if number is prime
function M.is_prime(n)
if n <= 1 then return false end
if n <= 3 then return true end
if n % 2 == 0 or n % 3 == 0 then return false end
local i = 5
while i * i <= n do
if n % i == 0 or n % (i + 2) == 0 then
return false
end
i = i + 6
end
return true
end
return MFile I/O Module
-- file_ops.lua
local M = {}
function M.read_file(path)
local file = io.open(path, "r")
if not file then
error("Cannot open file: " .. path)
end
local content = file:read("*all")
file:close()
return content
end
function M.write_file(path, content)
local file = io.open(path, "w")
if not file then
error("Cannot open file for writing: " .. path)
end
file:write(content)
file:close()
end
function M.file_exists(path)
local file = io.open(path, "r")
if file then
file:close()
return true
end
return false
end
function M.get_file_lines(path)
local lines = {}
local file = io.open(path, "r")
if not file then
return lines
end
for line in file:lines() do
table.insert(lines, line)
end
file:close()
return lines
end
return MUsing Multiple Modules
-- main_app.lua
local math_utils = require("math_utils")
local file_ops = require("file_ops")
local config = require("config")
-- Initialize configuration
config.init({debug = true, timeout = 60})
-- Process some data
local numbers = {5, 10, 15, 20}
local results = {}
for i, num in ipairs(numbers) do
table.insert(results, {
original = num,
factorial = math_utils.factorial(num),
is_prime = math_utils.is_prime(num)
})
config.debug("Processed " .. num)
end
-- Save results to file
local json_data = require("json").encode(results)
file_ops.write_file("results.json", json_data)
print("Processing complete. Results saved to results.json")Module Management Tips
- Use local variables for frequently used modules
- Keep modules focused on a single responsibility
- Document your module API clearly
- Handle errors gracefully in module functions
- Version your modules when they might change
- Test modules independently before integration
Next Steps
Now that you understand modules, explore LuaRocks to discover and install third-party Lua modules.
For more module information, see the Lua manual.
Last updated on