Nanfeng

Notes on software development, code, and curious ideas

Object-Oriented Programming in Lua

Lua has no built-in class declaration, but tables have identity and mutable state, while functions and metatables provide behavior. Together they support several object models without forcing one on the language.

Methods and self

The colon syntax passes the receiver as the first argument:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
local Account = {}
Account.__index = Account

function Account.new(owner, balance)
return setmetatable({ owner = owner, balance = balance or 0 }, Account)
end

function Account:deposit(amount)
assert(amount > 0, "amount must be positive")
self.balance = self.balance + amount
end

local account = Account.new("Ada", 100)
account:deposit(25) -- Account.deposit(account, 25)

The instance delegates missing fields to Account through __index, so methods do not need to be copied into every object.

Inheritance

A derived class can itself delegate to a base class:

1
2
3
4
5
6
7
8
9
10
11
12
local Savings = setmetatable({}, { __index = Account })
Savings.__index = Savings

function Savings.new(owner, balance, rate)
local object = Account.new(owner, balance)
object.rate = rate
return setmetatable(object, Savings)
end

function Savings:addInterest()
self.balance = self.balance * (1 + self.rate)
end

Multiple inheritance is possible with a custom __index function that searches several parents, but name conflicts and lookup cost grow quickly. Composition is often easier to understand.

Private state

Lexical closures provide privacy that callers cannot bypass through ordinary table access:

1
2
3
4
5
6
7
local function newCounter()
local value = 0
return {
increment = function() value = value + 1 end,
get = function() return value end
}
end

This creates new closures per object, trading memory for strong encapsulation. A weak-key table keyed by public objects is another way to hold private data.

Single-method objects

Sometimes an object needs only one operation. Return a closure directly instead of constructing a class:

1
2
3
4
5
6
local function newAccumulator(total)
return function(value)
total = total + value
return total
end
end

Choose the smallest model that expresses the ownership and behavior clearly. Lua’s flexibility is useful, but consistency within a codebase matters more than imitating every feature of a class-based language.

+