Skip to main content

Common lua idioms

· 3 min read

I have been using lua for a while. (About 4 years as of writing this!)
It's a language that I kinda grew up on, and I've learned to love.
This article will contain a list of idioms that are common to see in lua.
Lets go!

Default values with or

local x = obj.x or 15
-- in lua, accessing an undefined table value defaults to `nil`.
-- `nil` and `false` are falsey values,
-- So in this example,
-- If `obj` doesnt have the `.x`, then it will fall back to 15.

-- another common use case:
obj.x = obj.x or 0


Optional arguments:

In lua, passing less or more arguments doesn't error.

local function f(x)
print(x)
end

f(1,2,3) -- this is fine. output: 1
f() -- this is fine. output: nil

We can use this to have optional arguments:

-- euclidean distance:
local function distance(x, y, z)
z = z or 0 -- z is an optional argument!
local dx, dy, dz = x*x, y*y, z*z
return (dx+dy+dz) ^ 0.5
end


print(distance(3,4)) -- 5
print(distance(3,4,5)) -- 7.071

Error on undefined access

In lua, if you access an undefined variable, that variable returns nil. Example:

print(undefined_variable_34389439) -- nil
-- no error!

This is a bit weird, and some people don't like it.
But we can change this with __index and setmetatable.

In lua, _G is the special table that holds all the globals.

local function err(t,k,v)
error("Accessed an undefined variable: " .. tostring(k))
end
setmetatable(_G, {
__index = err
})

Now, if we access an undefined variable:

print(undefined_variable_34389439)
-- Error: main.lua:4:
-- Accessed an undefined variable: undefined_variable_34389439

You can do the same with __newindex if you want to stop pollution of the global namespace on accident.


Private class methods

For the OOP folks out there, who want private methods.

Often in lua, you'll see a class setup like so:

local MyClass = Class()

function Class:init()
self.x = 0; self.y = 0
end

function Class:privateHi()
-- ugh, I wish this could be private!
print(self)
end

function Class:hi()
self:privateHi()
print("hiiii")
end

Private methods can be done like so.


local function privateHi(self)
-- yay, now i am private!
print(self)
end

function Class:hi()
privateHi(self)
print("hiiii")
end

At first, you may feel uneasy doing it this way, or you may feel cheated.
Realize though, that all functions in lua are anonymous and first-class. privateHi has the exact same bytecode as Class:privateHi:

0000    FUNCF    4 
0001 GGET 1 0 ; "print"
0002 MOV 3 0
0003 CALL 1 1 2
0004 RET0 0 1

The only difference is that one is inside a table, and one is a local.