Overloading Operators with Metamethods
Beyond __index and __newindex, Lua defines a family of metamethods that let ordinary tables respond to operators the way built-in numbers and strings do. Writing my_vector + other_vector, my_matrix == other_matrix, or tostring(my_object) all work on plain tables once the appropriate metamethod -- __add, __eq, __tostring, and so on -- is defined in the table's metatable. This is Lua's version of operator overloading: there is no special class syntax for it, just metamethods that the interpreter checks automatically whenever the corresponding operator or standard function is used on a table.
Cricket analogy: The Duckworth-Lewis-Stern method redefines how 'target score' is calculated when rain interrupts a match, overriding the sport's default arithmetic just as __add overrides the default meaning of the + operator for a custom Lua type.
Arithmetic Metamethods
Arithmetic metamethods include __add, __sub, __mul, __div, __mod, __pow, and __unm (for unary negation), each corresponding directly to +, -, *, /, %, ^, and unary -. When Lua evaluates a + b, it first checks whether a is a number; if not (or if b isn't), it looks for __add in either operand's metatable and calls it as function(a, b), so a common pattern is defining a Vector class where v1 + v2 returns a brand-new vector whose components are the sums of v1's and v2's, without mutating either input.
Cricket analogy: Combining two batsmen's partnership contributions into a total isn't just their runs added in isolation -- strike rotation and boundary counts matter too, similar to how __add lets a Vector's + operator compute a genuinely new combined result rather than a naive sum.
Comparison and Concatenation Metamethods
Comparison metamethods __eq, __lt, and __le back the ==, <, and <= operators respectively (> and >= are implemented by Lua automatically flipping the operands into __lt/__le calls), but note that __eq is only even consulted when both operands are tables (or both userdata) of the same primitive type -- Lua never calls __eq to compare a table against a number or string. Separately, __concat handles the .. operator when at least one operand isn't a string or number, which lets you define how a custom object should be stringified and joined, for instance letting 'Score: ' .. scoreObject work naturally instead of raising a type error.
Cricket analogy: Deciding whether two bowling figures are 'equal' for a record book requires comparing overs, maidens, runs, and wickets together as one unit, not just one number, similar to how __eq lets two Lua tables define custom multi-field equality instead of Lua's default identity check.
__tostring and __call
The __tostring metamethod is called automatically by the built-in tostring() function (and therefore by print(), since print calls tostring on each argument), letting an object return a human-readable string like 'Vector(3, 4)' instead of the default 'table: 0x...' address. The __call metamethod goes further: if it's defined, you can invoke a table directly as if it were a function, obj(args), and Lua calls __call(obj, args) behind the scenes -- a technique used to build callable objects such as memoized function wrappers or configurable event handlers.
Cricket analogy: A broadcast graphic shows 'V Kohli: 82(61)' instead of a raw database row, just as __tostring turns a table into a readable string for print(); a bowling machine you 'call' with a speed setting to fire a delivery is like __call letting an object be invoked directly.
local Vector = {}
Vector.__index = Vector
function Vector.new(x, y)
return setmetatable({x = x, y = y}, Vector)
end
-- Overload + for Vector objects
function Vector.__add(a, b)
return Vector.new(a.x + b.x, a.y + b.y)
end
-- Overload == for Vector objects
function Vector.__eq(a, b)
return a.x == b.x and a.y == b.y
end
-- Readable printing
function Vector.__tostring(v)
return string.format("Vector(%g, %g)", v.x, v.y)
end
local v1 = Vector.new(1, 2)
local v2 = Vector.new(3, 4)
local v3 = v1 + v2
print(v3) --> Vector(4, 6)
print(v1 == Vector.new(1, 2)) --> true
-- __call: make an object directly invokable
local Counter = setmetatable({ count = 0 }, {
__call = function(self, step)
self.count = self.count + (step or 1)
return self.count
end
})
print(Counter()) --> 1
print(Counter(5)) --> 6
__eq is only consulted when both operands are tables (or both are userdata) -- Lua never calls __eq to compare a table with a number, string, boolean, or nil; such comparisons are always false without invoking any metamethod. Also, in Lua 5.3 and later, __eq is only triggered when the two tables are not already primitively equal (i.e., not the exact same table reference), since identical references are always == regardless of metamethods.
- Arithmetic metamethods (__add, __sub, __mul, __div, __mod, __pow, __unm) let custom tables respond to +, -, *, /, %, ^, and unary minus.
- Comparison metamethods __eq, __lt, and __le back ==, <, and <=; Lua derives > and >= by flipping operands.
- __eq only fires when both operands are tables of the same primitive type; comparing a table to a non-table never invokes it.
- __concat handles .. when at least one operand isn't already a string or number, enabling readable concatenation of custom objects.
- __tostring is called by tostring() and therefore by print(), letting objects control their own readable representation.
- __call lets a table be invoked like a function, obj(args), which is used for callable wrappers, memoization, and configurable handlers.
Practice what you learned
1. Which metamethod is invoked for the expression a + b when a is a table with an __add field?
2. When does Lua invoke __eq to compare a and b?
3. What does print(myObject) use to decide how to display myObject?
4. What does defining __call on a table's metatable allow you to do?
5. Are > and >= implemented with their own dedicated metamethods __gt and __ge in standard Lua?
Was this page helpful?
You May Also Like
Metatables Explained
Discover how Lua metatables let ordinary tables gain custom behavior for indexing, inheritance, and protection, powering nearly every advanced Lua idiom.
Tables in Lua
Learn how Lua's single, versatile data structure -- the table -- powers arrays, dictionaries, objects, and modules across every Lua program.
String Library in Lua
Master Lua's built-in string library -- immutable strings, extraction, case conversion, lightweight pattern matching, and formatted output.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics