5. Functions¶
Learning Objectives
- be familiar with Lua functions
- understand how to use functions as a tool for abstraction and deduplication
- understand the uses of higher order functions
- know when to use closures to represent stateful functions
Functions¶
Functions in Lua are the main tool for forming abstractions. We define functions similarly to other programming languages.
-- function foo which takes two parameters and returns their sum
function foo(x, y)
return x + y
end
Functions are really useful as abstractions over complex processes. By writing good functions with clear documentation, we allow other programmers to use our function without needing to understand the internals of the implementation.
Functions also allow us to consolidate repeated code into a single unit which can be modified once and have its changed replicated for every instance where its called.
Note that, unlike some other languages, Lua does not support function overloading.
Common mistake
Lua does not throw any errors when we pass less or more arguments than necessary. Suppose we have the previous function foo(x, y)
then we have this mapping from arguments to parameters:
Default arguments
We can use the Lua's nonstrict argument checking to provide default arguments to functions.
Multiple results
Functions can also return multiple results which can be stored in a multi-assignment statement.
Similarly to variable arguments, excess variables will have the valuenil
Since functions are considered first-class citizens in Lua, we can do a lot of interesting things with them.
Callbacks¶
Callbacks are functions passed as arguments to another function which we call a higher order function.
For instance, we can have the higher-order function compute
which takes in numbers x, y
and a binary function we call operation
and returns operation(x, y)
whatever the operation may be:
-- higher order function
function compute(x, y, operation)
return operation(x, y)
end
-- callbacks
local add = function(a, b) return a + b end
local mult = function(a, b) return a * b end
-- call compute with different callbacks
print(compute(5, 10, add)) -- output: 15
print(compute(5, 10, mult)) -- output: 50
We can use this pattern to design many more flexible and powerful functions.
Anonymous functions¶
Functions can also be anonymous, meaning that they do not have to be declared with a name. A very common usecase for anonymous functions is to pass them to higher-order functions as callbacks without having to name them.
-- we pass in an anonymous subtraction function as a callback
print(compute(5, 10, function (a, b) return a - b end)) -- output: -5
Naming anonymous functions
While less common, we can also name anonymous functions by assigning them to a variable. We usually do this when we are returning a function.
Closures¶
Lua support closures, which are functions which capture and "remember" the environment in which they were created. Closures are particularly useful for representing stateful functions such as a counter.
function createCounter(start)
local count = start or 0 -- let 0 be the default counter start value
return function() -- return a function with count variable captured
count = count + 1
return count
end
end
local counter = createCounter(20) -- create a counter starting at 20
print(counter()) -- output: 21, the state has changed
print(counter()) -- output: 22, the state has changed again
Local functions¶
Interestingly, functions can also be local which means that they follow the same scoping rules as local variables.
For instance, if we declare a local function inside of a function, the local function is not accessible from outside.
This is of course useful to prevent inner functions like helper functions from polluting the namespace.