Lua doesn’t have true ternary operator but there is a way to mimic the behavior. This is one of the biggest issues people who aren’t used to Lua have when reading Lua code. For the most part Lua is simple and straight forward to understand. Assuming a blocky and easy to understand style is used. However, the pseudo ternary is often used and trips up people. So let’s look at it in some detail.

The core of a ternary is an if ... else statement. In Lua, we can mimic this using and ... or with the evaluated statement. What we end of is if true then use the code path after and or if false use the code path after the or. Let’s look at a simple example.

if a == nil then
    b
else
    c
end

This is the equivalent to:

a == nil and b or c

Let’s look at more complex logic.

if a ~= nil and (1 < 5) then
    b
else
    c
end

We can do the same thing with the and ... or syntax. We just need to use parenthesis like we would with an if statement to separate what applies to which part of the statement

(a ~= nil and (1 < 2 or 4-2 > 5)) and b or c

We do need to be very careful with the separation. For this to work it always has to be (statement) and (statement) or (statement). Notice the and and or cannot be within parentheses. Here is a bad example that doesn’t work like you’d expect.

a ~= nil and (b or c) .. a

In this situation we’d end up with b concatenated with a which is expected. However, if a == nil, then we would end up with false. This statement does not have a proper or for the false code path of the logic. Since there is no code path Lua returns false for in this situation. The proper way to handle sticking a tailing statement to the ternary is to put the entire operation into parentheses.

(a ~= nil and b or c) .. a

Regardless of which branch is chosen we’ll always get the variable concatenated with a.

Now let’s look at complex components components.

a ~= nil and (b .. c .. a) or (a .. c .. b)
a == nil and (b .. c .. a) or (a .. c .. b)

This goes back to proper use of parentheses. We have the decision, the and and or outside of any parentheses and the code paths themselves within to properly contain the logic.

Let’s put together some samples we can run to really demonstrate everything that’s been covered.

local a = "123"
local b = "xyz"
local c = "test"

print("Simple if")
if a == nil then
    print("", b)
else
    print("", c)
end
print("Equivalent simple ternary")
print("", a == nil and b or c)

print("Complex if")
if a ~= nil and (1 < 2 or 4-2 > 5) then
    print("", b)
else
    print("", c)
end
print("Equivalent complex ternary")
print("", (a ~= nil and (1 < 2 or 4-2 > 5)) and b or c)

print("Bad ternary")
print("", a ~= nil and (b or c) .. a)
print("", a == nil and (b or c) .. a)
print("Proper ternary")
print("", (a ~= nil and b or c) .. a)
print("", (a == nil and b or c) .. a)

print("Complex ternary")
print("", a ~= nil and (b .. c .. a) or (a .. c .. b))
print("", a == nil and (b .. c .. a) or (a .. c .. b))

Now for the output

Simple if
	test
Equivalent simple ternary
	test
Complex if
	xyz
Equivalent complex ternary
	xyz
Bad ternary
	xyz123
	false
Proper ternary
	xyz123
	test123
Complex ternary
	xyztest123
	123testxyz

We should keep looking at this a bit further. In the above examples only variables were used and they were all run though a print statement. Ternary operations should be small and will typically only use variables. Such as using it for assignment. However, functions can be run in the ture and false branches and care needs to be taken.

Let’s look at an example with two functions.

print("Functions 1")

function b_func()
    print("b_func", b)
end

function c_func()
    print("c_func", c)
end

print("", true and b_func() or c_func())

You might think only function b_func would run but that’s not what happens. b_func has no explicit return so implicitly it returns nil, which evolutes to false. true run the true branch (b_func), but since that branch returns nil/false it will then continue on and run the false branch (c_func). So we’ll end up with this output:

Functions 1
b_func	xyz
c_func	test
	nil

Both functions ran which is not at all what we’d want to happen. Also notice that since c_func also don’t have a return it returned nil and since it’s inside of a print statement nil is printed.

This can cause problems if you’re trying to run a function that either doesn’t have a return value or if it returns nil/false and you don’t care about the return. The false branch (c_func) will only run if the true branch (b_func) returns true.

This goes back to the fact this is not a true ternary but an evolution!

If we change b_func to return true like so.

function b_func()
    print("b_func", b)
    return true
end

Then run it again we’ll get this output.

Functions 1
b_func	xyz
	true

This is what we want to happen with only b_func running. We still see true printed which is the return of the function because we’re running the return value through print.

If you’re working with functions you need to be very careful with this pattern otherwise more than you expect might run. My advise is if you’re calling functions don’t use this pseudo ternary and instead use a true if block. Only use this for variable assignment.

However, even with variable assignment you need to be careful with the true branch.

e = nil
g = true and e or c

What do you think g will be, nil or test (c)? If you guessed test (c) you’re right. Again this is an evaluation. Our expressoin is true, go to the true (and) branch. That branch if false so evaluate the false (or) branch. Since it’s an or where either e or c for the evalution then c will run whenever after e if it is false.

The next question is how to assign nil in the true branch. As far as I’m aware you can’t. So, use an if statement if you need nil assignment. It’s imperative that you are sure variable used for assignment with this ternary pattern will never be nil and any functions will either return true or a value otherwise this won’t work like you expect.

Hopefully this makes ternaries logic can be used in Lua clear to everyone.