Part of Learn Lua with NeoVim

Learn Lua in Neovim: Logical Operators — and, or, not & Ternary Idiom | Episode 8

Sandy LaneSandy Lane

Video: Learn Lua in Neovim: Logical Operators — and, or, not & Ternary Idiom | Episode 8 by Taught by Celeste AI - AI Coding Coach

Take the quiz on the full lesson page
Test what you've read · interactive walkthrough

Lua Logical Operators: and, or, not, and the Ternary Idiom

and and or short-circuit. They return operands, not booleans. x and a or b is Lua's ternary — but watch the gotcha when a is false or nil.

Lua's and/or operators are unusually powerful because they don't just return true/false — they return one of their operands. That property is what makes the x and a or b "ternary" idiom work.

The basics

local age = 12
local height = 140

-- AND: both conditions must be true
if age >= 10 and height >= 130 then
  print("You can ride!")
else
  print("Sorry, not allowed.")
end

Two conditions joined by and — both must be true for the whole expression to be true. With 12 >= 10 (true) and 140 >= 130 (true), the body runs.

local has_ticket = true
local is_vip = false

if has_ticket or is_vip then
  print("Welcome to the park!")
else
  print("You need a ticket.")
end

or is "either one is enough." has_ticket is true, so the body runs regardless of is_vip.

NOT

local park_closed = false

if not park_closed then
  print("The park is open!")
end

not flips the truthiness. not false is true; not true is false; not nil is true (because nil is falsy).

not always returns a real boolean (true or false). and and or do not — that's the trick.

Short-circuit evaluation

print(1 and 2)        -- 2
print(1 or 2)         -- 1
print(false and 2)    -- false
print(false or 2)     -- 2
print(nil and 2)      -- nil
print(nil or 2)       -- 2

What's going on?

x and y — if x is falsy, returns x. Otherwise returns y.

x or y — if x is truthy, returns x. Otherwise returns y.

So:

  • 1 and 21 is truthy, so returns 2.
  • 1 or 21 is truthy, so returns 1 (doesn't even look at 2).
  • false and 2false is falsy, so returns false.
  • false or 2false is falsy, so returns 2.

This short-circuits — if the first operand decides the answer, the second isn't evaluated. That's a feature: it lets you write things like x and x.field (won't crash if x is nil).

The "ternary" idiom

Lua doesn't have a cond ? a : b ternary like C. It uses and/or:

local score = 85
local grade = score >= 60 and "Pass" or "Fail"
print("Result: " .. grade)

Walk it through:

  1. score >= 60 is true (truthy).
  2. true and "Pass" is "Pass".
  3. "Pass" or "Fail" is "Pass" (truthy first operand wins).

If score = 50:

  1. score >= 60 is false.
  2. false and "Pass" is false (skipped second arg).
  3. false or "Fail" is "Fail".

Result: "Fail". Same effect as (score >= 60) ? "Pass" : "Fail" in C.

The ternary gotcha

local enabled = true
local label = enabled and false or "default"
print(label)  -- "default"  (unexpected!)

The "true value" is false itself, which is falsy. So:

  1. enabled and falseenabled truthy, returns false.
  2. false or "default"false falsy, returns "default".

You wanted false, got "default". The and ... or idiom only works when the "true value" is itself truthy.

For correct behaviour with falsy "true values," use a real if:

local label
if enabled then
  label = false
else
  label = "default"
end

Verbose, but unambiguous.

Defaults with or

local function greet(name)
  name = name or "stranger"
  print("Hello, " .. name)
end

greet("Alice")    -- "Hello, Alice"
greet()           -- "Hello, stranger"

name or "stranger" returns name if it's truthy, otherwise "stranger". Standard Lua idiom for default arguments.

But beware: if name is the empty string "", it's truthy in Lua, so the default doesn't kick in:

greet("")    -- "Hello, " (empty greeting)

If empty strings should also use the default, check explicitly:

if name == nil or name == "" then
  name = "stranger"
end

Combining operators

local age = 18
local has_id = true
local is_friday = true

if (age >= 18 and has_id) or is_friday then
  print("Allowed")
end

Parentheses for clarity. Without them, and binds tighter than or (like * vs + in math), but readers shouldn't have to remember that.

Precedence in Lua, lowest to highest:

  1. or
  2. and
  3. <, >, <=, >=, ~=, ==
  4. .. (string concat)
  5. +, -
  6. *, /, //, %
  7. not, #, unary -
  8. ^

But again — parentheses for anything non-trivial.

Common stumbles

Expecting and/or to return booleans. They return operands. print(2 and 3) is 3, not true.

The ternary gotcha. x and a or b breaks when a is false or nil. Use a real if then.

Defaulting with or for empty strings. s or "default" doesn't fire when s = "" (empty string is truthy). Check s == nil or s == "".

Using && and ||. Those are C-style. Lua uses and, or, not — keywords, not symbols.

Precedence surprises. not x == y parses as (not x) == y, not not (x == y). Add parens.

What's next

Episode 9: numeric for loops. for i = 1, 10 do ... end, with optional step. A countdown program and a multiplication table.

Recap

and, or, not for boolean logic. and and or return operands, not booleans, and short-circuit. The Lua "ternary" is cond and a or b — but only safe when a is truthy. Defaults with name or "default" (watch the empty-string edge case). Precedence: or < and < comparisons; use parens for clarity.

Next episode: numeric for loops.

Ready? Take the quiz on the full lesson page →
Test what you've learned. Watch the lesson and try the interactive quiz on the same page.