Acquis 5 - Enums
Enums are first-class values representing named constants. Each enum value is a symbol: it is only equal to other values from the same enum with the same name.
Syntax
Declare an enum with the enum ... end expression:
local status = enum
pending, active, completed, failed
end
Values are comma-separated names. The enum expression returns the first value:
local color = enum red, green, blue end
assert(color == color.red) -- enum equals its first value
Identity and equality
Enum values are symbols with identity semantics. Values from the same enum with the same name are equal:
local e = enum x, y, z end
assert(e.x == e.x)
assert(e.y == e.y)
Different values from the same enum are not equal:
local e = enum a, b, c end
assert(e.a ~= e.b)
assert(e.b ~= e.c)
Values from different enums are never equal, even with the same name:
local e1 = enum a, b, c end
local e2 = enum a, b, c end
assert(e1.a ~= e2.a) -- different enums
assert(e1.b ~= e2.b)
Indexing
Access enum values by name:
local fruit = enum apple, orange, banana end
local f = fruit.orange
assert(f == fruit.orange)
Access by 1-based numeric index:
local size = enum small, medium, large end
assert(size[1] == size.small)
assert(size[2] == size.medium)
assert(size[3] == size.large)
Any enum value can be indexed to access other values from the same enum:
local e = enum x, y, z end
assert(e.x.y == e.y) -- name indexing from a value
assert(e.y.z == e.z)
assert(e[1][3] == e.z) -- numeric indexing from a value
Chained indexing
Values can be chained to navigate the enum:
local e = enum a, b, c end
assert(e.a.b.c == e.c)
assert(e.c.b.a == e.a)
Ordering
Enum values can be compared by their position:
local priority = enum low, medium, high, critical end
assert(priority.low < priority.medium)
assert(priority.medium < priority.high)
assert(priority.high < priority.critical)
assert(priority.critical >= priority.high)
assert(priority.low <= priority.low)
Ordering uses the declaration position, not the name.
Numeric conversion
Use tonumber to get the 1-based index of an enum value:
local state = enum idle, running, paused, stopped end
assert(tonumber(state.idle) == 1)
assert(tonumber(state.running) == 2)
assert(tonumber(state.paused) == 3)
assert(tonumber(state.stopped) == 4)
Type checking
The type function returns "enum" for enum values:
local e = enum val end
assert(type(e) == "enum")
assert(type(e.val) == "enum")
assert(type(e[1]) == "enum")
Usage patterns
As function arguments
local mode = enum read, write, append end
local function openFile(path, m)
if m == mode.read then
-- read logic
elseif m == mode.write then
-- write logic
end
end
openFile("data.txt", mode.read)
As table values
local status = enum pending, approved, rejected end
local requests = {
{ id = 1, status = status.approved },
{ id = 2, status = status.pending },
}
for _, req in ipairs(requests) do
if req.status == status.approved then
process(req)
end
end
As function returns
local result = enum success, failure, timeout end
local function fetch(url)
-- ...
return result.success
end
if fetch("https://example.com") == result.success then
print("Request succeeded")
end
Motivation
Lua does not have a built-in enum type. Developers simulate enums using tables or string constants, but these approaches lack type safety.
String constants
A common Lua pattern:
-- Lua: string constants
local STATUS_PENDING = "pending"
local STATUS_ACTIVE = "active"
local function setStatus(s)
if s == STATUS_PENDING then
-- ...
end
end
setStatus("pending") -- works
setStatus("penidng") -- typo: no error, just wrong
With enums, typos cause immediate errors:
-- Lus: type-safe enums
local status = enum pending, active end
setStatus(status.pending) -- works
setStatus(status.penidng) -- error: nil field
Numeric constants
Another Lua pattern:
-- Lua: numeric constants
local RED, GREEN, BLUE = 1, 2, 3
if color == RED then -- is this a color or a count?
Enums are distinct types, preventing confusion:
-- Lus: enums are symbols
local color = enum red, green, blue end
local count = 1
assert(color.red ~= count) -- never equal
Cross-enum safety
Different enums are incompatible:
local status = enum ok, error end
local color = enum red, green end
-- Same position (1), but never equal
assert(tonumber(status.ok) == tonumber(color.red))
assert(status.ok ~= color.red)
This prevents accidentally mixing unrelated constants.