Only nil and false are falsy¶
Level: 101 · anyone who has written if count in Python and expects 0 to be false
One line: Ruby has exactly two falsy values, nil and false; 0, 0.0, "", [] and {} are all truthy and no class can opt in to falsiness — where Python asks __bool__, then __len__, and calls all six of those values false.
Ruby decides truthiness once, in the interpreter, by identity: an object is falsy if it is nil or false and truthy otherwise. There is no protocol to consult, so if 0, if "" and if [] all take the then branch, x || default replaces only nil and false, and a class that defines ! changes what !obj returns without changing what if obj does. Python asks the object: bool(x) calls x.__bool__(), falls back to len(x) == 0, and only then to "true". That one difference produces most of the surprises a Python programmer meets in a first week of Ruby, and most of the ones a Ruby programmer meets going the other way.
Verified output of nil_false_and_truthiness_rb.rb — regenerated by tools/run_examples.py, never hand-typed.
1. the truth table: !!x
nil false
false false
0 true
0.0 true
"" true
[] true
{} true
:a true
2. nil is an object with methods
nil.to_s ""
nil.to_a []
nil.to_i 0
nil.inspect "nil"
3. || returns the first truthy operand
x = nil; x || "default" "default"
x = 0; x || "default" 0
x = ""; x || "default" ""
x = false; x || "default" "default"
4. nil and false are two different objects
nil == false false
nil.nil? true
false.nil? false
false == 0 false
5. truthiness is not a method a class can define
!h (Hollow defines !) true
h ? :truthy : :falsy :truthy
!!h (calls ! twice) false
[].empty? true
[] ? :truthy : :falsy :truthy
6. if 0 and if "" take the then branch
n = 0; n ? :then : :else :then
s = ""; s ? :then : :else :then
7. dropping nil is not dropping falsy
mixed.compact [1, false, 2]
mixed.select(&:itself) [1, 2]
Reading the output¶
- Row 1.
!!xis the idiom for "the truthiness ofx, as a boolean". Onlynilandfalsegivefalse;0,0.0, the empty string, the empty array, the empty hash and a symbol all givetrue. - Row 2.
nilis a real object.nil.to_sis the empty string,nil.to_athe empty array,nil.to_izero, andnil.inspectis the textnilthatpprints. - Row 3.
||returns its first truthy operand, sox || "default"replacesnilandfalseand nothing else:0and""survive. The||=idiom rests on this row, and so does its one trap — a legitimatefalsegets replaced too. - Row 4.
nilandfalseare different objects of different classes:nil == falseisfalse,nil.nil?is the test that singles outnil, andfalse == 0isfalsebecausefalseis not a number. - Row 5.
Hollowdefines!to returntrue, so!histrue— buth ? :truthy : :falsystill says:truthy, because a conditional never calls!. The trap in the middle:!!his!applied twice, so with a custom!it saysfalsefor an object everyiftreats as true.[]isempty?and truthy at the same time. - Row 6.
if 0andif ""run thethenbranch. Testing for "no items" in Ruby isif list.empty?, and for "no value" it isif x.nil?. - Row 7.
compactdropsnilonly;select(&:itself)dropsnilandfalse. Pick the one that says what you mean.
Compared with Python¶
Python's bool(x) disagrees with !!x on six of row 1's eight values: 0, 0.0, '', [] and {} are False along with None and False. That difference runs through every later row. x or "default" replaces 0 and "" too (row 3), which is why count = count or 10 is a Python bug when 0 is a legitimate count. False == 0 is True (row 4) — bool is a subclass of int, which the duck typing page prints as isinstance(True, int) — where Ruby's false == 0 is false. Truthiness is a protocol: Hollow.__bool__ makes h falsy in every position — not h, the conditional expression and not not h all agree (row 5) — whereas Ruby's ! override changed only !h. if 0 and if "" take the else branch (row 6). Rows 2 and 7 agree in shape: None is an object with str and repr, though without Ruby's forgiving to_a and to_i, and filtering out None is a different thing from filtering by truth in both languages.
Verified output of nil_false_and_truthiness_py.py — regenerated by tools/run_examples.py, never hand-typed.
1. the truth table: bool(x)
None False
False False
0 False
0.0 False
'' False
[] False
{} False
'a' (no symbols: a str) True
2. None is an object with methods
str(None) 'None'
list(None) (no nil.to_a) TypeError
int(None) (no nil.to_i) TypeError
repr(None) 'None'
3. or returns the first truthy operand
x = None; x or "default" 'default'
x = 0; x or "default" 'default'
x = ""; x or "default" 'default'
x = False; x or "default" 'default'
4. None and False are two different objects
None == False False
x = None; x is None True
x = False; x is None False
False == 0 True
5. truthiness is a method a class can define
not h (Hollow defines __bool__) True
'truthy' if h else 'falsy' 'falsy'
not not h False
len([]) 0
'truthy' if [] else 'falsy' 'falsy'
6. if 0 and if '' take the else branch
n = 0; 'then' if n else 'else' 'else'
s = ''; 'then' if s else 'else' 'else'
7. dropping None is not dropping falsy
[v for v in mixed if v is not None] [1, False, 2]
[v for v in mixed if v] [1, 2]
Python's rules are the Truth Value Testing section of the built-in types reference ↗; the Python library's glossary ↗ is the vocabulary on that side.
Try it¶
- Write
blank?(x)that is true fornil,false, an empty or whitespace-only string, and an empty array or hash — the idea Rails callsblank?— and print a table of!!xbesideblank?(x)fornil,false,0,""," ",[],{},"a",[nil]andtrue. Where do the two columns disagree? - A variable
flagmay legitimately holdfalse. Show thatflag || truecannot tell "unset" from "set to false", then write the replacement that fills in onlynil(flag.nil? ? default : flag) and print both versions forflag = nil,flag = falseandflag = true.
Solution to 1
nil_false_and_truthiness_kata_rb.rb in full — pasted here by tools/run_examples.py from the file CI runs.
# Kata: blank?(x) -- the idea Rails calls "blank": nil, false, an empty or
# whitespace-only string, an empty array or hash. Plain truthiness says only nil
# and false are falsy, so blank? has to ask each object what it is.
def blank?(x)
case x
when nil, false then true
when String then x.strip.empty?
else x.respond_to?(:empty?) ? x.empty? : false
end
end
puts " value truthy? blank?"
[nil, false, 0, "", " ", [], {}, "a", [nil], true].each do |x|
puts " #{x.inspect.ljust(8)} #{(!!x).to_s.ljust(8)} #{blank?(x)}"
end
Verified output of nil_false_and_truthiness_kata_rb.rb — regenerated by tools/run_examples.py, never hand-typed.
See also¶
- Everything is an object, even
1andnil— whynilhas methods at all - Ruby has four kinds of equality —
nil == falseis one of the four; the page on the other three and/orprecedence —||returns an operand, andorbinds lower than=||=and nil guards — the idiom built on row 3, and thefalsetrap inside it- Gotchas for Python programmers —
0and""being truthy is the first row of that list - NilClass ↗ — every method
nilanswers &.skips the call when the receiver is nil —receiver&.method(args)returnsnilwithout calling anything- Twenty-three Python traps for a Ruby programmer — The twenty-three places where a Ruby reflex gives the wrong answer in Python
- There is more than one way, and one obvious way — Ruby's idioms