Thinking with Functions — Introduction

I’m a major contributor to ScriptingHelpers.org, which helps ROBLOX Lua programmers with problems with their programming.

Unsurprisingly, most of the submitted code isn’t “good style,” since the programmers are newer. A big key to this is that functions aren’t used.

What are functions?

In most modern programming languages, what are called “functions” are really a mix of two concepts:

Mathematical functions — you put one thing in, and you get one thing out. Square-root is a function. Dividing by two is a function. Taking the mean of a sequence of numbers is a function.

Procedures — sequences of actions. This essentially helps you “group” code together under a name, so that your code is easier to understand.

Lua’s Functions

A quick review, for those not familiar with Lua. In Lua, functions look like this:

function name(argumentOne, argumentTwo)
-- Body
end

name is the name of the function, arguments are the stuff coming in to the function, and the body is what the function does.

You call (use) a function like this:

name() -- No arguments
name(one) -- One argument
name(one, two) -- Two arguments

How do I use functions to make my code better?

Start by describing the whole thing of what you want to do. For example,

I want players to be given a particular uniform when they step on a block

Now, break it into the pieces of what need to be done:

Do this to any player that touches this block:
Remove their current Pants and Shirts
Give them this Pants and Shirt.

Now we know that we need (at least) three functions: dressPlayer, removeUniform, giveUniform. This makes the code very simple and clear:

function dressPlayer(character)
removeUniform(character)
giveUniform(character)
end
function removeUniform(character)
for _, child in pairs(character:GetChildren()) do
if child:IsA("Clothing") or child:IsA("ShirtGraphic") then
child:Destroy()
end
end
end
function giveUniform(character)
local pants = Instance.new("Pants", character);
local shirt = Instance.new("Shirt", character);
end
function partTouched(part)
if part.Parent and part.Parent:FindFirstChild("Humanoid") then
dressPlayer(part.Parent)
end
end
script.Parent.Touched:connect(partTouched)

From the function names, it’s incredibly clear what each is supposed to be doing, and because the longest is four lines long, it’s also easy to read each and determine what they’re doing.

In other words, this is the wrong way to solve this problem:

function partTouched(part)
if part.Parent and part.Parent:FindFirstChild(“Humanoid”) then
for _, child in pairs(part.Parent:GetChildren()) do
if child:IsA(“Clothing”) or child:IsA(“ShirtGraphic”) then
child:Destroy()
end
end
local pants = Instance.new(“Pants”, part.Parent);
local shirt = Instance.new(“Shirt”, part.Parent);
end
end
script.Parent.Touched:connect(partTouched)

The first hint that the above is probably not an elegant solution is the name. partTouched doesn’t really say what is happening here — complicated things are happening, and we don’t really know is happening without reading carefully. Compare this version to the previous one, which just calls dressPlayer — and we know what was intended, without even writing any comments (though you should always do this too)!

TL;DR

List what you want to accomplish.

Give each small task its own function — use many short functions, not one (or zero) long ones.

Give your functions names that describe what they do.

Show your support

Clapping shows how much you appreciated Blue Shaman’s story.