Errors and pcall
So far, the errors have come from Lua itself: calling nil, adding nil… Your own functions can report errors too. When a function gets values it can't work with, it's better to stop with a clear message than to carry on with a wrong result. That's what error is for:
local coins = 20
local function buySeeds(price, bags)
local cost = price * bags
if cost > coins then
error("Not enough coins")
end
coins = coins - cost
return coins
end
print(buySeeds(2, 5))
print(buySeeds(5, 4))
print("Done")This prints:
10
main.lua:6: Not enough coins
stack traceback:
[C]: in function 'error'
main.lua:6: in local 'buySeeds'
main.lua:13: in main chunkerror stops the program, just like an error from Lua: Done is never printed. Lua puts main.lua:6: in front of your message: the line where error was called. The traceback shows how the program got there; read it from the bottom: the main program called buySeeds on line 13, and on line 6, buySeeds called error. ([C] means a function built into Lua, which is written in the C language.) When you don't want the position in the message, pass 0 as the second argument: error("Not enough coins", 0).
“If this isn't true, stop with an error” is such a common check that it has a shortcut: assert(condition, message). When the condition is true, assert does nothing; when it's false or nil, it ends with the message:
local function plantSeeds(crop, plots)
assert(plots > 0, "Plant at least one plot")
print("Planting " .. crop .. " on " .. plots .. " plots")
end
plantSeeds("carrot", 3)
plantSeeds("wheat", 0)This prints:
Planting carrot on 3 plots
main.lua:2: Plant at least one plotSometimes an error shouldn't end the whole program. Emma can't afford the pumpkin seeds, but she still wants to buy the others. pcall (protected call) calls a function and catches its error. Give it the function and then its arguments: pcall(buySeeds, 5, 4) calls buySeeds(5, 4). pcall returns several values: when everything goes well, true and whatever the function returned; when there's an error, false and the error message. Either way, the program carries on:
local coins = 20
local function buySeeds(price, bags)
local cost = price * bags
if cost > coins then
error("Not enough coins")
end
coins = coins - cost
return coins
end
print(pcall(buySeeds, 2, 5))
local ok, result = pcall(buySeeds, 5, 4)
if ok then
print("Coins left: " .. result)
else
print("Error: " .. result)
end
print("Done")This prints:
true 10
Error: main.lua:6: Not enough coins
DoneThe usual way is the second one: store the values in ok and result, and decide by ok what result is. A typical mistake is writing pcall(buySeeds(5, 4)). With the parentheses after buySeeds, Lua calls the function right away, before pcall, so its error isn't caught. As with any function you pass to another one, pass the function itself and its arguments separately.
Instructions
Emma's coins drop below zero. In buySeeds, before she pays, end with error("Not enough coins for " .. crop, 0) when cost is greater than coins. Then the second purchase would stop the whole program, so call all three purchases through pcall and print what it returns, for example print(pcall(buySeeds, "carrot", 2, 5)). The program prints:
true 10
false Not enough coins for pumpkin
true 6Emma can't buy zero bags. At the start of buySeeds, add assert with the condition bags > 0 and the message "Buy at least one bag". Then, at the end of the program, print pcall(buySeeds, "corn", 3, 0). The new line is false and the message with its position, for example:
false main.lua:5: Buy at least one bagWrite a function tryToBuy with the parameters crop, price and bags. It calls buySeeds through pcall and stores the results in ok and result. When the purchase works, it prints Bought … seeds, coins left: …; otherwise, it prints Can't buy … seeds: and the error message. At the end of the program, call tryToBuy("strawberry", 4, 1) and tryToBuy("potato", 3, 2). The program ends with:
Bought strawberry seeds, coins left: 2
Can't buy potato seeds: Not enough coins for potatoStart programming for free
10/11