Variables, types, functions as first-class values, closures, and string manipulation. The foundation you need to write real Lua.
Lua is one of the most widely embedded scripting languages in the world. It powers game scripting in Roblox, World of Warcraft addons, and Angry Birds. It's the scripting layer in Neovim, Redis, nginx (via OpenResty), and dozens of embedded systems. Lua is tiny — the entire interpreter is under 300KB — but it's fast and expressive.
lua or luajit interpreter.Lua has eight types: nil, boolean, number, string, function, userdata, thread, and table. Variables are dynamically typed. The type() function returns a string naming the type of any value.
-- Single-line comment
--[[ Multi-line
comment ]]
-- Variables are global by default
x = 10
name = "Lua"
-- Use 'local' for block-scoped variables (always prefer this)
local count = 0
local pi = 3.14159
local greeting = "Hello, " .. name -- string concatenation with ..
-- nil means "no value"
local nothing = nil
-- Check types
print(type(x)) --> number
print(type(greeting)) --> string
print(type(nothing)) --> nil
print(type(true)) --> boolean
print(type(print)) --> function
-- Numbers are always floats internally in Lua 5.3+
-- (Lua 5.3+ has a distinct integer subtype)
print(type(10)) --> number
print(math.type(10)) --> integer
print(math.type(10.0)) --> float
Lua uses if/elseif/else/end, while/do/end, repeat/until, and numeric or generic for loops. There is no switch or case — use elseif chains or a table dispatch.
-- if / elseif / else
local score = 85
if score >= 90 then
print("A")
elseif score >= 80 then
print("B")
else
print("C or below")
end
-- Numeric for: from, to, [step]
for i = 1, 5 do
io.write(i .. " ") -- no newline
end
print() -- newline
-- Count down
for i = 10, 1, -2 do
io.write(i .. " ")
end
print()
-- while loop
local n = 1
while n <= 4 do
n = n * 2
end
print(n) --> 8
-- repeat / until (condition checked AFTER body)
local x = 0
repeat
x = x + 1
until x >= 3
print(x) --> 3
-- break exits any loop
for i = 1, 100 do
if i > 5 then break end
io.write(i .. " ")
end
print()
Functions are first-class values in Lua. They can be stored in variables, passed as arguments, and returned from other functions. Lua supports multiple return values natively — no tuple wrapper needed.
-- Standard function definition
local function greet(name)
return "Hello, " .. name .. "!"
end
print(greet("world")) --> Hello, world!
-- Functions are values — assign them
local add = function(a, b) return a + b end
print(add(3, 4)) --> 7
-- Multiple return values
local function minmax(t)
local lo, hi = t[1], t[1]
for _, v in ipairs(t) do
if v < lo then lo = v end
if v > hi then hi = v end
end
return lo, hi
end
local lo, hi = minmax({5, 2, 8, 1, 9, 3})
print(lo, hi) --> 1 9
-- Varargs (variable number of arguments)
local function sum(...)
local args = {...} -- pack into table
local total = 0
for _, v in ipairs(args) do
total = total + v
end
return total
end
print(sum(1, 2, 3, 4, 5)) --> 15
-- select('#', ...) returns argument count
local function count_args(...)
return select('#', ...)
end
print(count_args(10, 20, 30)) --> 3
A closure is a function that captures variables from its enclosing scope (called upvalues). Closures are the primary way to create stateful functions and simulate private state in Lua.
-- Counter factory — each call creates independent state
local function make_counter(start)
local count = start or 0 -- 'count' is an upvalue
return {
inc = function() count = count + 1 end,
dec = function() count = count - 1 end,
get = function() return count end,
}
end
local c1 = make_counter(0)
local c2 = make_counter(100)
c1.inc(); c1.inc(); c1.inc()
c2.dec()
print(c1.get()) --> 3
print(c2.get()) --> 99
-- Closures capture by reference, not by value
local function make_adder(n)
return function(x) return x + n end
end
local add5 = make_adder(5)
local add10 = make_adder(10)
print(add5(3)) --> 8
print(add10(3)) --> 13
local. In Lua, variables without local are global — they live in the global table _G and can be accidentally read or overwritten by any other code. Declare everything local by default; only use globals when you explicitly need them.The string standard library provides pattern matching (similar to regex but simpler), formatting, and manipulation. Strings in Lua are immutable and interned.
local s = "Hello, Lua World!"
-- Length
print(#s) --> 17
print(string.len(s)) --> 17
-- Case
print(string.upper(s)) --> HELLO, LUA WORLD!
print(string.lower(s)) --> hello, lua world!
-- Substrings (1-indexed, negative from end)
print(string.sub(s, 1, 5)) --> Hello
print(string.sub(s, -6)) --> World!
-- Find (returns start, end positions)
local i, j = string.find(s, "Lua")
print(i, j) --> 8 10
-- Pattern matching with gmatch (iterate over matches)
local sentence = "the quick brown fox"
for word in string.gmatch(sentence, "%a+") do
io.write("[" .. word .. "] ")
end
print() --> [the] [quick] [brown] [fox]
-- gsub (global substitution)
local result, count = string.gsub(s, "%a+", function(w)
return string.upper(w)
end)
print(result) --> HELLO, LUA WORLD!
print(count) --> 3
-- string.format (like printf)
local formatted = string.format("Pi = %.4f, Count = %d", math.pi, 42)
print(formatted) --> Pi = 3.1416, Count = 42
-- Method syntax: s:upper() == string.upper(s)
print(s:sub(1, 5):upper()) --> HELLO
word_freq(text) that takes a string and returns a table mapping each word to its count. Use string.gmatch and string.lower.top_n(freq, n) that takes the frequency table and returns the top n words sorted by count descending. Use table.sort.word_freq that accepts a list of stopwords (e.g., {"the", "a", "is"}) and skips them.word_freq and top_n as keys. Return it at the bottom of the file.Implement a simple printf-style function using string.format and varargs. It should accept a format string and any number of additional arguments, then print the formatted result. Bonus: make it return the string instead of printing it, and add a second wrapper that prints.
The foundations from today carry directly into Day 2. In the next session the focus shifts to Day 2 — building directly on everything covered here.
Before moving on, verify you can answer these without looking:
Live Bootcamp
Learn this in person — 2 days, 5 cities
Thu–Fri sessions in Denver, Los Angeles, New York, Chicago, and Dallas. $1,490 per seat. June–October 2026.
Reserve Your Seat →