Functions

By the end of this lesson you'll define and call functions, return several values at once , accept any number of arguments with ... , give arguments sensible defaults, pass functions around as values, capture state in closures , and solve nested problems with recursion — the toolkit behind every real Lua script.

Learn Python, JavaScript, Java and more with free interactive lessons, real projects and AI-powered help. Beginner-friendly.

Part of the free Lua course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

A function is a recipe card . The ingredients you hand it are the arguments ; the finished dish it gives back is the return value . Write the recipe once and you can cook it any night without re-deriving it. Lua's recipes have two superpowers most kitchens lack: one card can plate up several dishes at once (multiple returns), and a recipe can keep a private pantry that remembers what you used last time (a closure ). Master the recipe card and you stop repeating yourself.

1. Defining and Calling Functions

A function bundles a piece of work under a name so you can run it again whenever you like. The shape is function name(args) ... end , and you put local in front to keep it scoped — the same safe default you learned for variables. Inside, return hands a value back to whoever called it; a function with no return still runs (often for a side effect like printing) and quietly gives back nil . Read this worked example and run it.

2. Multiple Return Values

Here's something most languages can't do: a Lua function can return several values at once — just separate them with commas after return . You catch them by listing the same number of variables on the left: local q, r = divide(17, 5) . There's one rule that trips everyone up: a function call is cut down to a single value unless it's the last thing in a list . Put the multi-return call last and all its values survive; put it earlier and only the first is kept.

Your turn. Finish the minMax function so it returns the smaller value first and the larger second, then catch both returned values. Fill in the blanks marked ___ and compare with the expected output.

3. Varargs: Any Number of Arguments

Sometimes you don't know in advance how many arguments a function will get — think print , which accepts as many as you throw at it. Lua's answer is varargs , written as ... (three dots) in the parameter list. Pack them into a table with {' '} so you can loop over them, count them with select("#", ...) , or read from a position with select(i, ...) . When some arguments might be nil , use table.pack(...) instead — it records an accurate count in .n that {' '} can't promise.

Now you try. Build an average function that accepts any number of scores using ... , sums them, and divides by how many there were. Fill in the three blanks:

4. Default Argument Values

Lua has no special syntax for default arguments, but there's a one-line idiom every Lua programmer uses: x = x or fallback . The or operator returns its first truthy value, so when an argument is missing (it arrives as nil ) the fallback kicks in. The single gotcha: or also treats false as "missing", so for boolean flags compare against nil directly ( if flag == nil then ... end ) — otherwise a deliberately passed false would be overwritten.

5. Functions as Values & Closures

In Lua, functions are first-class values : you can store one in a variable, pass it into another function, return it, or drop it in a table — exactly like a number or a string. This unlocks the closure : a function that remembers the local variables alive where it was created. Those captured variables stay private and persist between calls, which is how you build counters and accumulators without reaching for globals. Crucially, each closure gets its own copy of the captured state.

6. Recursion

Recursion is a function that calls itself, solving a big problem by reducing it to a smaller version of the same problem. Every recursive function needs two parts: a base case that stops the chain, and a recursive step that moves toward that base case by shrinking the input. Forget the base case and the calls never stop — you'll get a stack overflow . Recursion is the natural fit for anything nested: folder trees, JSON, or maths like factorial defined in terms of itself.

No blanks this time — just a brief and an outline to keep you on track. You'll combine three of this lesson's ideas: the default-argument idiom, returning multiple functions, and closures that share captured state. Build it, run it, and check your output against the example in the comments.

Practice quiz

Which keyword starts the return of a value from a Lua function?

  • yield
  • return
  • give
  • out

Answer: return. return hands a value back to the caller.

How do you make a function scoped to its block?

  • scoped function
  • private function
  • local function
  • static function

Answer: local function. local function keeps the function out of the global namespace.

How do you capture both values of return a, b ?

  • local x = f()

List two variables on the left to receive both returns.

A multi-return call keeps all its values only when it is...

  • The last item in a list
  • The first item in a list
  • Wrapped in parentheses
  • Assigned to one variable

Answer: The last item in a list. Calls are truncated to one value unless they are last in a list.

What does ... mean in a parameter list?

  • A range
  • Varargs: any number of extra arguments
  • A default value
  • A comment

Answer: Varargs: any number of extra arguments. ... collects any number of extra arguments passed by the caller.

Which idiom gives a default for a missing argument?

  • x = default(x)
  • x = x and default
  • x = x or default
  • x ??= default

Answer: x = x or default. or returns its first truthy value, so a nil argument falls back to the default.

What is a closure?

  • A loop that closes itself
  • A type of table
  • A way to end a function early
  • A function that remembers variables from where it was created

Answer: A function that remembers variables from where it was created. A closure captures and keeps the local variables alive where it was made.

Every recursive function must have a...

  • Base case that stops it
  • Global counter
  • Vararg parameter
  • Return of nil

Answer: Base case that stops it. Without a base case the recursion never stops and overflows the stack.

What does select("#", ...) return?

  • The first vararg
  • The last vararg
  • How many varargs were passed
  • A packed table

Answer: How many varargs were passed. select("#", ...) counts the varargs received.

A function with no return statement gives back...

  • 0
  • An empty string
  • An error
  • nil

Answer: nil. With no return, a Lua function hands back nil.