5.4.1 Passing and Returning Functions

You might want a function to behave a bit differently depending on the arguments you give it. Or maybe you want a function to call some other function after running its task.

For instance, sorting a list of items is a very common task. But there are also many different sorting algorithms, each with its own advantages and disadvantages.

So you might have some intermediate function to handle processing the data and another that handles the actual sorting.

Because functions can be treated like data, they can be passed as into other functions just as you would a normal argument.

Lua
local function someFunction(numList, func1, func2)
	local rand = math.random(2)
	
	if rand == 1 then
		func1(numList)
	else
		func2(numList)
	end
end

local function printSum(numList)
	local sum = 0
	
	for _, v in ipairs(numList) do
		sum = sum + v
	end
	print(sum)
end

local function printProduct(numList)
	local product = 1
	
	for _, v in ipairs(numList) do
		product = product * v
	end
	print(product)
end

local numberList = {1, 2, 4, 8, 16}

someFunction(numberList, printSum, printProduct)

First we define a primary function that performs some task. In this case, just choose a random number. We give it two more functions which, one of which will be run, depending on the result.

Because the result might not be known ahead of time or we might not know when the main function will be completed, we can supply the function argument to be invoked by the main function.


Returning a Function

Just like functions can be passed as arguments, functions can also be returned from a function.

For example, models and parts require a different method of moving. So we can create a “getter” function that returns the proper move function depending on the object type.

Lua
local objectList = {}

--------just initilization stuffs------
do
	local model = Instance.new("Model")
	local modelPart = Instance.new("Part")
	modelPart.Position = Vector3.new(5, 5, 0)
	modelPart.Anchored = true
	modelPart.Parent = model
	model.PrimaryPart = modelPart
	model.Parent = game.Workspace
	
	local part = Instance.new("Part")
	part.Position = Vector3.new(-5, 5, 0)
	part.Anchored = true
	part.Parent = game.Workspace
	
	objectList[1] = model
	objectList[2] = part
end
--------------------------------------

local moveAmount = Vector3.new(0, 0, -0.25)
local runTimeLength = 10

local function moveModel(model)
	model:TranslateBy(moveAmount)
end

local function movePart(part)
	part.Position = part.Position + moveAmount
end

local function getMoveFunction(object)
	if object.ClassName == "Model" then
		return moveModel
	else
		return movePart
	end
end

local stopTime = tick() + runTimeLength

while tick() < stopTime do
	for _, object in ipairs(objectList) do
		local tempFunction = getMoveFunction(object)
		tempFunction(object)
	end
	task.wait()
end

Because models are made of a collection of parts that need to move and rotate as a group, we cannot move just one part. But trying to move all parts by computing their translation and orientation is error prone.

So the model object provides several methods to move or rotate the entire model as a whole. In line 27, the ‘TranslateBy’ method is one such method.

On the other hand, parts can be be moved directly just by setting its position.


Okay, but why can’t we just use an if statement and call the function directly?

Because this a bad contrived example, that’s why. Now stop interrupting me so we can get to the part where this all comes together and makes sense.