Home Courses Lua Day 1
Day 01 Foundations

Day 1

Day 1

~1 hour Intermediate Hands-on Precision AI Academy

Today's Objective

Variables, types, functions as first-class values, closures, and string manipulation. The foundation you need to write real Lua.

Why 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.

Today's topics: variables and types, operators, control flow, functions, closures, varargs, and the string standard library. Everything runs in a plain lua or luajit interpreter.

Variables and Types

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.

lua_-_variables_and_types.txt
LUA — VARIABLES AND TYPES
-- 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

Control Flow

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.

lua_-_control_flow.txt
LUA — CONTROL FLOW
-- 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

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.

lua_-_functions.txt
LUA — FUNCTIONS
-- 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

Closures

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.

lua_-_closures_and_upvalues.txt
LUA — CLOSURES AND UPVALUES
-- 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
Always use 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.

String Library

The string standard library provides pattern matching (similar to regex but simpler), formatting, and manipulation. Strings in Lua are immutable and interned.

lua_-_string_library.txt
LUA — STRING LIBRARY
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
Exercise
Build a Word Frequency Counter
  1. Write a function word_freq(text) that takes a string and returns a table mapping each word to its count. Use string.gmatch and string.lower.
  2. Write a function top_n(freq, n) that takes the frequency table and returns the top n words sorted by count descending. Use table.sort.
  3. Test it with a paragraph of text — at least 50 words. Print the top 5 words and their counts.
  4. Add a parameter to word_freq that accepts a list of stopwords (e.g., {"the", "a", "is"}) and skips them.
  5. Wrap the whole thing into a module: a table with 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.

What's Next

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.

Supporting Videos & Reading

Go deeper with these external references.

Day 1 Checkpoint

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 →
Continue To Day 2
Day 2