Conditional Statements 101 (+Luau Ternary)

EDIT

I have rewritten the entire post to educate better on conditional statements in specifics.

When I started, I learned to script from tutorials, examining others’ scripts, and just simply practice. I didn’t know anything more than just making code like a language given to the computer. Now, I wanted to learn more about how the computer thinks (computer science). Therefore, I have updated this tutorial to be more technical, as I feel it is easier to understand.

I have additionally included ternary statements as a Luau addition.

This tutorial targets beginners.

Welcome to Conditional/Boolean statements 101.

Boolean/conditional statements

While “conditional statement” is loosely defined, it could be an if-statement or actually the same as a boolean statement. This is up to context but for the sake of this guide, a conditional statement is an if-statement. A boolean statement is basically what’s between if and then in your conditional statements.

A boolean statement is a statement such as:
player.Name ~= "YourName" (~= is opposite of ==)
frame.Visible == true (== is opposite of ~=)

It does not have to contain operators like >, <, ~=, or ==. It can just be this:
workspace:FindFirstChild("special part")

Boolean statements evaluate to a boolean

When a boolean statement is used in code, it will evaluate as true or false (the only two boolean values).

What it means to evaluate as true or false is similar to an expression evaluating to a given value. You can think of it as equaling true or false. If you print the boolean statement, you do get true or false.

Conditional statements with no operator

Boolean statements in conditional statements without an operator will check if the statement is false or nil. Example here:

if (player) then
    -- player exists because it is not nil. it is also not the boolean value false.
end

The statement becomes a boolean statement without an operator, which is dependent on this rule.

This is particularly useful for quickly writing conditional statements where you test the existence of some object (is not nil) or shorthand to check an expected boolean value (is not false). Do not use this shorthand if it is important that you differentiate between false and nil evaluation in the conditional statement.

However, when outside a conditional statement, it instead evaluates to the object or value in the statement that does exist or evaluate to true. For example:

local fruit = apple or bear.isFruit = true

We know apples are fruits so fruit evaluates to apple. In the event that apple wasn’t considered a fruit and bears are also not fruits, it evaluates to false entirely.

Keyword not, negating a boolean value

A boolean value is like a switch. In binary, a boolean is a 0 or 1. 1 is on, 0 is off. 1 is true, 0 is false.

If you use not on any boolean statement such as apple.isFruit() which is true (hopefully), you will receive false instead. (not apple.IsFruit()false)

The same is true for any non-operator statements that needed to be converted to booleans like objects. An example is not apple which turns into false as well, even when assigned to a variable.

The idea is just converting to its opposite, just flip the switch.

Many beginners often write this code:

if Frame.Visible == true then
    Frame.Visible = false
else
    Frame.Visible = true
end

You can shorten this to:

Frame.Visible = not Frame.Visible

Trust me, it will make your life way easier.

Boolean Statement Table


(from codeacademy)

This table lists all the possible results from using the boolean statement keywords comparing just two booleans in each scenario.

Ternary statements

A ternary statement in Luau is simply an in-line if-statement assigned as a variable.

You can also evaluate the ternary statement to a false or nil if you wanted with this where otherwise a boolean statement assigned to a variable couldn’t.

There is another tutorial that covers it in detail if you’re interested.

Readability

You must always consider the readability of your code when using assigned boolean statements outside if-statements and ternary statements.

A really long if-statement composed of multiple elseif statements in addition for example, is better just left as an if-statement spread out. Never assign it to a variable or property.

In my personal opinion, ternary statements are only better than if-statements if the statement is just one if statement and the boolean statement inside contains less than three values at most. It is up to you how you decide readability.

Logic gates

Interested in how boolean works inside the computer? The keywords are logic gates.

End

Now you know what conditional statements do. Use that boolean logic.

OLD tutorial archive

What is a conditional statement?

A conditional statement is a statement that is evaluated based on using not, ==, ~=. When used in while loops and if statements, it will return true or false based on if the given comparison is correct. For example:

if player.Name == "YourName" then
    print("Found the player named YourName!")
end

This utilizes == as a comparison from the player’s name and “YourName” which is a string (text).

~= is the opposite as it states that the given compared variables do not equal each other. For example:

if player.Name ~= "YourName" then
    print("The player was not YourName!")
end

not on one variable will make it compare to the other variable using either == or ~= as if comparing the “truth” of the value to the opposite, so in this case it inverts not "YourName" into false since "YourName" was already truthy.

if player.Name == not "YourName" end

This is does the same as the ~= sign statement though its cleaner to use ~= and is not the same in terms of that it is converted to a untruthy value, ~= is more represented as something like:

not (player.Name == “YourName”)

Why? Consider this usage case where the player’s name is “YourName”:

player.Name ~= "YourName" -- evaluates to false
player.Name == not "YourName" -- evaluates to false

player.Name ~= "Bob" -- evaluates to true
player.Name == not "Bob" -- evaluates to false!!

Logically, not “Bob” is just an inversion that becomes a boolean value of false in our case which differentiates it from ~= which returned true in this case when used.

You can also use > and < as comparison signs, <= for less than or equal to and >= for greater than or equal to.

You can also compare something’s existence like this:

if object then
    print("object exists")
end

This if statement becomes correct if the object exists or the given object is false. You can also use not on it to make a statement that checks if it doesn’t exist, it will prove true if the object is false.

Lastly, you can use and/or to create more statements. For example this will check if the object given is either string:

if object == "Red" or object == "Blue" then
    print("Object is red or blue")
end

Similarly for and it must prove both statements:

if object:IsA("BasePart") and object.Name == "Blue" then
    print("Object is a base part and is named Blue")
end

You can combine them all,

if type(object) == "userdata" and object.Name == "Blue" or object == "Blue" then
    print("Object given is called Blue")
end

You can learn more here as I won’t cover it all. It will cover else and elseif statements as well but I only covered what will be required for this tutorial since I won’t focus on if statements.

Conditional statements without loops or if statements

Onto the actual tutorial, the statements you create can be variables and aren’t limited to what they’re used in like loops and if statements. I think they are called comparators but I would need clarification. You can use them like this:

local condition = object == "StringExample"
if condition then
    print("Object is StringExample")
end

You can use all the comparisons like ~= and >=. and/or works as well.

local comparison = object ~= nil and "Something" or "SomethingElse"

The difference is, I am not using an if statement or requiring a true/false comparison, but something to return. The way it works is that it will travel each ~= and == statement you put until you have a part of the statement that is no longer using a comparison and returns that, if you have an or then the value after the or statement if its not being compared. This is because the comparison will return true or false so you would just be returning a boolean.

So the code will return either Something or SomethingElse depending on if the object is nil or not.

However, when you would compare something against another variable without a comparison like == or ~=, you are comparing if it is not nil or not false, so you can remove ~= nil here as a better way to check the object’s existence and it’ll do the job the same as long as object can’t be a boolean value.

local comparison = object and "Something" or "SomethingElse"

You can use all comparisons but it will return only the values that aren’t comparisons. If you were to use only comparisons:

local boolean = object == "yes" or object == "no"

It would only return true or false depending on if the object is either “yes” or “no”.

For the conditional statements that we don’t want to use a boolean for, you can use it in many practical ways.

You can for example fill values depending on a statement.

local object = "blue" -- pretend we don't know its "blue"
local newCFrame = CFrame.new(object == "blue" and 215 or 100, object == "blue" and 424 or 91, object == "blue" and 111 or 125)

It will fill values in a CFrame like CFrame.new(215, 424, 111) if object is “blue”, otherwise it will create CFrame.new(100, 91, 125). You can use this in Vector3s and practically anything else.

Significant example use

You can shorten this logic

if gui.Visible == true then
    gui.Visible = false
else
    gui.Visible = true
end

To:

gui.Visible = not gui.Visible
36 Likes

I think, as a prime example, and one of the most useful things you can do with this is toggling booleans.

local value = true
value = not value --> value == false
value = not value --> value == true again

Great for basic UI state:

Frame.Visible = not Frame.Visible

As opposed to what I’ve seen so many people do:

if Frame.Visible == true then
    Frame.Visible = false
else
    Frame.Visible = true
end

Although in this example:

You don’t even need the ~= nil part; anything not nil (or false) is truthy!

local comparison = object and "Something" or "SomethingElse"

Great tutorial though. I think this is something a lot of beginners aren’t aware of.

4 Likes

Thank you for the clarification, I am just bad at examples so the ~= nil is just part of my example so necessarily you are right but I meant to say that any value can replace what nil is there as an example. I have not actually thought of = not value which is silly of a tutorial that requires knowing that so I will add that into the tutorial.

Also according to your example clarification, I guess I got it wrong that you need a comparison to get the value after and then. I’ll fix my tutorial to that.

1 Like

That’s not how not works. This is effectively doing player.Name == false because not inverts "YourName" (a truthy value) into false.

2 Likes

I edited and clarified that, thanks for the pointer. I also fixed it according to @ClockworkSquirrel’s reply.

Hmm. So, what this post is really about is avoiding conditional statements by substituting logical statements that can be directly evalulated, often with the benefit of not even having to completely evaluate them (short-circuit evaluation, which even Lua does). This is useful in runtime environments where such a substitution actually does eliminate conditional branching, and therefore branch misprediction.But what about Lua?

So, those are the comparison operators, not the conditional statement itself. Whether or not something is conditional is really about whether or not execution has to branch.

Where it gets tricky in Lua (and other languages) is that just because you got rid of the if keyword, I’m not sure you’re actually removing conditional branching. (Edit note: I realize the OP was just showing syntactial alternatives and not making performance improvement claims, my questions aim to explore the performance implications, not criticize the original post in any way). This:

a = (b or c) and d or e

where a,b,c,d, and e are all boolean, is the sort of statement where the right-hand expression could be evaluated without conditional branching, such as in a strictly-typed language with a boolean type that guarantees pure bit-wise logical evaluation is valid. But what about Lua, where there is no such guarantee? And what about something like this:

local name = (b or c) and "John" or "Bob"

Do you think this can still be evaluated as a logical expression, or is it just a conditional branching cleverly disguised as a logical expression? In an untyped language with little to no optimization, is this even a different case that the a,b,c,d,e example above? Like, consider the first example, but I tell you that a, b, and c are boolean true or false, but variable d is storing the string “John” and e stores “Bob”!

I don’t want to discount big performance gains you can get by finding direct-evaluation expressions that can replace branching conditional logic in some runtime environments, most obviously strictly-typed, compiled languages. But I’m honestly not sure how much of a difference it makes in Lua. I’d actually like to hear the answer to this, from someone who has implemented a Lua interpreter.

Have you benchmarked any of these substituations to see if these sorts of substitutions are actually improving performance meaningfully?

2 Likes

Just gonna jump in, there’s still branching. ==, and, etc, all branch internally in the bytecode. local a = x or y generates roughly the same code as

local a = x
if not a then
    a = y
end

Cool, thanks. I kinda figured it must still be branching; I can’t think of any way you could avoid it, especially when you consider how in Lua,

true and "Elmo"

evaluates to Elmo. The truth of “everything besides nil and false is logically true” doesn’t fully describe that case, since it could also allow for (true and "Elmo") to evaluate to true, which is certainly the case in other languages where using a non-boolean value with a boolean operator implicitly converts it to a boolean first. A boolean binary operator expression evaluating to something like “Elmo” is, well, unusual.

Careful now, these are not equivalent statements! Consider the case where the player’s name is, say, ArtFoundation:

player.Name ~= "ArtFoundation" -- evaluates to false
player.Name == not "ArtFoundation" -- evaluates to false

player.Name ~= "Bob" -- evaluates to true
player.Name == not "Bob" -- evaluates to false!!

The logical equivalent of the ~= statement is:

not (player.Name == "ArtFoundation")

Why? Because logically inverting “Bob” does not give you “ArtFoundation”. (not "Bob") is simply the value false.

It has been edited to correct this mistake, thanks for the heads up! I tried to aim this for beginners and felt like the beginner myself :grimacing:

1 Like

Excuse me for the bump but one thing I want to mention is that sometimes attaching statements to variables can speed up readability of a code. It may cause miniscule performance but using this is a lot better than spending extra minutes reading something.

GuiObject.Activated:Connect(function(input)
   local DidTouch = input.UserInputType == Enum.UserInputType.Touch
   local DidClick = input.UserInputType == Enum.UserInputType.MouseButton1

   if DidTouch or DidClick then
    -- SomeSomethingFunction()
   end
end

-- As opposed to..
GuiObject.Activated:Connect(function(input)
   --// For newbies, you have no idea what's going on here.
   if input.UserInputType == Enum.UserInputType.Touch or input.UserInputType == Enum.UserInputType.MouseButton1 then
    -- SomeSomethingFunction()
   end
end

I have been there a lot of times where I overestimated my abilities and chosen to use the second method. But I didn’t account for the other codes I’d have to remember and so it was a nightmare trying to read my scripts all around.

Edit: If you’re new to programming, do yourself a favor and choose the first method.

1 Like