Modules in Lua
Modules are the key to organizing and reusing code in Lua. They
allow you
to split your code into separate files, create libraries, and manage
dependencies.
Lua's module system is simple yet powerful, using tables and the
require()
function. In this lesson, you'll learn how to create, use, and organize modules
effectively. Let's dive in!
What are Modules?
A module is simply a Lua file that returns a table containing functions and values:
-- mymodule.lua
local M = {}
function M.greet(name)
return "Hello, " .. name
end
function M.add(a, b)
return a + b
end
M.version = "1.0"
return M
- Create a local table (usually named
Mor the module name) - Add functions and values to the table
- Return the table at the end
Using require()
Load modules with the require() function:
-- main.lua
local mymodule = require("mymodule")
print(mymodule.greet("Alice")) -- Hello, Alice
print(mymodule.add(5, 3)) -- 8
print(mymodule.version) -- 1.0
require() caches modules. If you require the
same
module multiple times, it only loads once and returns the cached version.
Click Run to execute your code
Module Patterns
1. Basic Table Module
-- math_utils.lua
local M = {}
function M.square(x)
return x * x
end
function M.cube(x)
return x * x * x
end
return M
2. Module with Private Functions
-- calculator.lua
local M = {}
-- Private function (not exported)
local function validate(x)
return type(x) == "number"
end
-- Public functions
function M.add(a, b)
if not (validate(a) and validate(b)) then
error("Arguments must be numbers")
end
return a + b
end
function M.multiply(a, b)
if not (validate(a) and validate(b)) then
error("Arguments must be numbers")
end
return a * b
end
return M
3. Module with State
-- counter.lua
local M = {}
local count = 0 -- Private state
function M.increment()
count = count + 1
return count
end
function M.decrement()
count = count - 1
return count
end
function M.get()
return count
end
function M.reset()
count = 0
end
return M
Click Run to execute your code
Class Modules
Modules can export classes:
-- person.lua
local Person = {}
Person.__index = Person
function Person:new(name, age)
local self = setmetatable({}, Person)
self.name = name
self.age = age
return self
end
function Person:greet()
return "Hello, I'm " .. self.name
end
function Person:birthday()
self.age = self.age + 1
end
return Person
-- main.lua
local Person = require("person")
local alice = Person:new("Alice", 25)
print(alice:greet()) -- Hello, I'm Alice
alice:birthday()
print(alice.age) -- 26
Module Organization
Directory Structure
myapp/
├── main.lua
├── lib/
│ ├── utils.lua
│ ├── database.lua
│ └── models/
│ ├── user.lua
│ └── product.lua
└── config.lua
Loading from Subdirectories
-- Use dot notation for subdirectories
local User = require("lib.models.user")
local utils = require("lib.utils")
package.path. Use forward slashes or dots for subdirectories, not
backslashes.
Practical Examples
String Utilities Module
-- string_utils.lua
local M = {}
function M.trim(s)
return s:match("^%s*(.-)%s*$")
end
function M.split(s, delimiter)
local result = {}
local pattern = string.format("([^%s]+)", delimiter)
for match in s:gmatch(pattern) do
table.insert(result, match)
end
return result
end
function M.capitalize(s)
return s:sub(1, 1):upper() .. s:sub(2):lower()
end
function M.reverse(s)
return s:reverse()
end
return M
Configuration Module
-- config.lua
local M = {}
M.app = {
name = "MyApp",
version = "1.0.0",
debug = true
}
M.database = {
host = "localhost",
port = 5432,
name = "mydb"
}
M.server = {
host = "0.0.0.0",
port = 8080
}
function M.get(key)
local keys = {}
for k in key:gmatch("[^.]+") do
table.insert(keys, k)
end
local value = M
for i, k in ipairs(keys) do
value = value[k]
if not value then return nil end
end
return value
end
return M
Logger Module
-- logger.lua
local M = {}
local levels = {
DEBUG = 1,
INFO = 2,
WARN = 3,
ERROR = 4
}
local currentLevel = levels.INFO
function M.setLevel(level)
currentLevel = levels[level] or levels.INFO
end
local function log(level, message)
if levels[level] >= currentLevel then
print(string.format("[%s] %s: %s",
os.date("%Y-%m-%d %H:%M:%S"),
level,
message))
end
end
function M.debug(message)
log("DEBUG", message)
end
function M.info(message)
log("INFO", message)
end
function M.warn(message)
log("WARN", message)
end
function M.error(message)
log("ERROR", message)
end
return M
Click Run to execute your code
Module Best Practices
- Always use
localfor the module table - Return the module table at the end
- Keep private functions local (don't add to module table)
- Use meaningful module names
- Document your module's public API
- Avoid global variables in modules
- Keep modules focused on a single responsibility
Module Template
-- module_name.lua
-- Description: What this module does
-- Author: Your name
-- Version: 1.0
local M = {}
-- Private variables
local privateVar = "private"
-- Private functions
local function privateFunction()
-- Implementation
end
-- Public functions
function M.publicFunction()
-- Implementation
privateFunction() -- Can call private functions
end
-- Constants
M.VERSION = "1.0"
M.CONSTANT = "value"
return M
Practice Exercise
Try these module challenges:
Click Run to execute your code
Summary
In this lesson, you learned:
- What modules are and why they're important
- Using
require()to load modules - Different module patterns (basic, private functions, state)
- Creating class modules
- Organizing modules in directories
- Practical examples: string utils, config, logger
- Module best practices and templates
What's Next?
Congratulations on completing Module 5! You've mastered OOP and modules in Lua. Next, we'll explore advanced topics including error handling, debugging, and best practices for writing robust Lua code. Let's continue! 🚀
Enjoying these tutorials?