Back

Activity 1.7: Functions

divider

The Idea

Functions look how you would guess, and you should write local function, not function — the same global-by-default rule from Activity 1.2 applies to function names too.

main.lua
local function area(w, h)
return w * h
end
print(area(3, 4))
Output
12

More than one answer

Here is Lua's best trick, and one most languages cannot do at all. A function can return several values, and the caller decides how many to keep.

main.lua
local function stats(a, b)
return a + b, a * b, math.max(a, b)
end
print(stats(3, 4))
local sum, product = stats(3, 4)
print(sum, product)
Output
7 12 4
7 12

No wrapper object, no unpacking step. You met this already without being told — Activity 1.3's code:find('NEST') returned two numbers, and that is why.

The rule for how many you get: ask for fewer than were returned and the extras are thrown away; ask for more and the missing ones are nil. It never errors either way.

Arguments are just as forgiving

Which is a mixed blessing. Lua does not check how many arguments you passed.

main.lua
local function show(a, b)
print(a, b)
end
show(1)
show(1, 2, 3)
Output
1 nil
1 2

Neither call was an error. A missing argument arrives as nil and an extra one is silently dropped. This is the same family of problem as the misspelled variable in Activity 1.2: the program keeps running with a hole in it.

The upside is that Lua's default-argument idiom falls out of it for free, using the or behavior from Activity 1.5.

main.lua
local function greet(name)
name = name or "stranger"
return "Hello, " .. name
end
print(greet("Ada"))
print(greet())
Output
Hello, Ada
Hello, stranger

The catch you should expect by now: x = x or default also replaces a deliberately-passed false. For a boolean argument you need if x == nil then instead, which asks the question you actually meant.

Functions are values

Activity 1.2 showed that type(print) is function. Here is what that was for.

main.lua
local function double(n)
return n * 2
end
local operation = double
print(operation(21))
local function apply(f, value)
return f(value)
end
print(apply(double, 5))
Output
42
10

A function can be stored in a variable, passed to another function, and returned from one. Nothing about a function is special — it is a value like 5 or "hello". That single fact is what the next activity is built on, and it is how LÖVE will call your code in Unit 2.

Taking however many you get

Three dots in the parameter list collects any number of arguments. select("#", ...) counts them.

main.lua
local function count(...)
return select("#", ...)
end
print(count(1, 2, 3), count())
Output
3 0

You will not need this often, and it is here mostly so it is not a mystery when you read someone else's Lua. print itself is written this way.

divider

Build

Start a fresh main.lua. You are rebuilding Activity 1.4's jump computer as a function — which is the point, because a function can be asked more than once.


Task 1: Return two things at once

  • Write a jumpCost function taking a distance and a rate.
  • Have it return both the fuel needed and the number of tanks to load, from one return.
  • Catch both values into two variables and print them.
main.lua
-- main.lua - Skynest jump planner
local function jumpCost(distance, rate)
local fuel = distance * rate
local tanks = math.ceil(fuel / 40)
return fuel, tanks
end
local fuel, tanks = jumpCost(47, 2.5)
print("Fuel:", fuel)
print("Tanks:", tanks)
Output
Fuel: 117.5
Tanks: 3

Task 2: Make the rate optional

  • Default the rate to 2.5 when the caller leaves it out.
  • Change your call so it passes only the distance.
  • The output should not change at all. That is the test — you added flexibility without changing behavior.
main.lua
-- main.lua - Skynest jump planner
local function jumpCost(distance, rate)
rate = rate or 2.5
local fuel = distance * rate
local tanks = math.ceil(fuel / 40)
return fuel, tanks
end
local fuel, tanks = jumpCost(47)
print("Fuel:", fuel)
print("Tanks:", tanks)
Output
Fuel: 117.5
Tanks: 3

Task 3: Feed both returns straight into print

  • Call jumpCost again at a premium rate of 3, this time inside a print rather than storing the results first.
  • Both returned values should appear. A call in the last argument position expands to all of its returns.
  • Then try moving it to the front, as print(jumpCost(47, 3), "premium"), and see how many values survive. This trips up experienced Lua programmers, so it is worth seeing once on purpose.
main.lua
-- main.lua - Skynest jump planner
local function jumpCost(distance, rate)
rate = rate or 2.5
local fuel = distance * rate
local tanks = math.ceil(fuel / 40)
return fuel, tanks
end
local fuel, tanks = jumpCost(47)
print("Fuel:", fuel)
print("Tanks:", tanks)
print("Premium:", jumpCost(47, 3))

Challenge (Optional)

  • Add a third return value: a status string of "OK" or "OVER BUDGET" depending on whether the tanks exceed 3. Then call it and keep only the first and third values.
  • Write a describe function that takes another function plus a distance, calls it, and prints the result in a sentence. Pass jumpCost into it.
divider

Check Your Work

Your finished file should produce this.

Output
Fuel: 117.5
Tanks: 3
Premium: 141 4

Three values on that last line means both returns arrived from one call. If you only got the fuel figure, your call was not in the last argument position — which is the lesson, not a mistake.