Learn Lua in Neovim: Booleans, Nil & Comparisons — The Truthy Gotcha | Episode 5
Video: Learn Lua in Neovim: Booleans, Nil & Comparisons — The Truthy Gotcha | Episode 5 by Taught by Celeste AI - AI Coding Coach
Lua Booleans, Nil, and Comparisons: The Truthy Gotcha
Comparison operators return booleans.
==and~=for equality. Onlyfalseandnilare falsy —0,"", and empty tables are all truthy. The opposite of most languages.
Lua's truthiness rules are simple but different from C, Python, and JavaScript. If you've programmed in those, this episode is the one that catches you off guard.
Comparison operators
print(10 > 5) -- true
print(10 < 5) -- false
print(10 == 10) -- true
print(10 ~= 10) -- false (~= is "not equal")
Six comparison operators:
| Operator | Meaning |
|---|---|
== |
equal |
~= |
not equal |
< |
less than |
> |
greater than |
<= |
less than or equal |
>= |
greater than or equal |
Note ~= instead of !=. (!= is a syntax error.)
Each returns a boolean — true or false.
A real comparison
local age = 18
print(age >= 18) -- true
print(age <= 17) -- false
Use these in if statements (next episode) to make decisions.
The truthy gotcha
if 0 then
print("0 is truthy!")
end
if "" then
print("empty string is truthy!")
end
if nil then
print("this never prints")
else
print("nil is falsy")
end
Output:
0 is truthy!
empty string is truthy!
nil is falsy
In Lua, only false and nil are falsy. Everything else is truthy.
That includes:
0— truthy""(empty string) — truthy{}(empty table) — truthy- Any function reference — truthy
This trips up programmers from C, Python, and JavaScript, where 0 and "" are falsy. In Lua, treat 0 and "" as values, not as "falsy markers."
Why does this matter?
local count = 0
if count then
print("we have a count") -- runs! count is 0, but truthy.
end
If you want "0 means no count," check explicitly:
if count > 0 then
print("we have items")
end
Or for "did this variable get set":
if count ~= nil then
print("count exists")
end
The Lua idiom for "if a variable is set" is if x then — relying on nil being the only "unset" sentinel. That's why globals default to nil and reading an undeclared variable returns nil rather than erroring.
Equality is strict
print(1 == "1") -- false (different types)
print(1 == 1.0) -- true (number == number, integers and floats compare)
print(nil == false) -- false (different types)
Lua doesn't auto-convert types for ==. JavaScript's == infamously converts (1 == "1" is true in JS). Lua doesn't — different types are never equal.
The exception: integers and floats. 1 == 1.0 is true because they're both number type, just different representations.
String comparison: lexicographic
print("apple" < "banana") -- true
print("Apple" < "apple") -- true (uppercase comes first in ASCII)
print("10" < "2") -- true (string compare, '1' < '2')
Strings compare byte-by-byte. Uppercase letters have lower ASCII codes than lowercase. Numbers-as-strings sort lexically, not numerically — "10" < "2" because '1' < '2'.
For natural sorting (numeric within strings), you'd convert and compare numerically.
Boolean operators (preview)
print(true and false) -- false
print(true or false) -- true
print(not true) -- false
and, or, not — full episode on these next time. They're more interesting than they look because of short-circuit evaluation and the truthy/falsy rules.
Common stumbles
Treating 0 as falsy. if count is true even when count = 0. Use if count > 0 if you mean "non-zero."
Treating "" as falsy. if s is true even when s = "". Use if s ~= "" for "non-empty string."
Using != for inequality. It's a syntax error in Lua. Use ~=.
Comparing different types with ==. Always returns false, even when the values "look equal" (1 == "1" is false). Convert explicitly.
Forgetting nil propagates. nil > 5 is an error, not false. Comparisons require both sides to be the right type. nil ~= 5 is true, however — ~= and == work on any types.
A safer pattern
local function is_set(x)
return x ~= nil
end
if is_set(count) and count > 0 then
print("count exists and is positive")
end
Two checks: "is it set" and "what value does it have." Combining the truthy/falsy rules with explicit comparisons keeps the intent clear.
What's next
Episode 6: comments and user input. Single-line --, multi-line --[[ ... ]], and reading from the user with io.read() plus tonumber().
Recap
Six comparison operators, all returning booleans: == ~= < > <= >=. Only false and nil are falsy — 0, "", and {} are all truthy. Lua doesn't auto-convert types; 1 ~= "1". String comparison is lexicographic by byte. Use ~=, not !=, for "not equal."
Next episode: comments and io.read().