Perl Part I · Module 3 · Operators and Expressions
Logical Operators: && || // and the Word Forms
Perl has two complete sets of logical operators: the symbols and the words. They do the same logic and they do not have the same precedence, which means swapping one for the other can change what a line does without changing what it says. The demonstration is two lines apart: assigning the result of an or-expression works with the symbol and silently fails with the word. Beyond that, these operators have two properties that are easy to state and easy to forget — they stop evaluating as soon as the answer is known, which makes them a flow-control tool rather than only a logic tool, and they hand back one of their operands rather than a true or false, which is why defaulting with them works and why defaulting a count with the wrong one destroys a measurement.
Foundation15 min readPerlPart IFundamentalsLogicShort-CircuitOperators
Part I · Module 3 · Chapter 3.8 · Logical Operators: && || // and the Word Forms
1. Two Sets of the Same Logic
| Meaning | Symbol | Word |
|---|---|---|
| both | && | and |
| either | || | or |
| not | ! | not |
There is also //, defined-or, which you met as //= in Chapter 3.4 and which §6 returns to.
The symbol and word forms implement identical logic. They do not have identical precedence, and that is the entire content of §5 — the most important section in the chapter. Treat them as two tools that happen to share a meaning, not as spellings of one tool.
2. Perl's True and False Have Specific Values
Before combining conditions, it is worth seeing what the result of a logical operation actually is:
use strict;
use warnings;
my $t = !0;
my $f = !1;
print "!0 is [$t] with length ", length($t), "\n";
print "!1 is [$f] with length ", length($f), "\n";!0 is [1] with length 1
!1 is [] with length 0Perl's canonical true is the number 1; its canonical false is the empty string. That is consistent with Chapter 2.8, where the empty string was one of the few false values, and it explains something you may have noticed: printing a false condition prints nothing at all rather than the word "false".
3. Short-Circuit Evaluation
&& and || stop as soon as the answer is decided. This is not an optimisation detail — it is observable, and the way to observe it is a side effect:
use strict;
use warnings;
sub noisy {
my ($label) = @_;
print " evaluated: $label\n";
return 1;
}
print "false && noisy:\n";
my $r1 = 0 && noisy("right of &&");
print "true || noisy:\n";
my $r2 = 1 || noisy("right of ||");
print "true && noisy:\n";
my $r3 = 1 && noisy("right of && when left true");
print "false || noisy:\n";
my $r4 = 0 || noisy("right of || when left false");false && noisy:
true || noisy:
true && noisy:
evaluated: right of && when left true
false || noisy:
evaluated: right of || when left falseThe first two cases printed nothing from inside noisy, which is the proof: the function was never called.
&&— if the left operand is false the result must be false, so the right operand is not evaluated.||— if the left operand is true the result must be true, so the right operand is not evaluated.
Subroutines are Module 10's subject; the one here exists only to make the evaluation visible. The important consequence is that the right-hand side of a logical operator is conditional code, and Perl programmers use it that way deliberately. Chapter 3.9 is devoted to the most common instance of that.
4. They Return an Operand, Not a Boolean
This is the second surprising property, and it is what makes || useful for more than yes/no questions:
use strict;
use warnings;
print "0 || 'def' -> [", (0 || "def"), "]\n";
print "'a' || 'b' -> [", ("a" || "b"), "]\n";
print "1 && 2 -> [", (1 && 2), "]\n";
print "0 && 2 -> [", (0 && 2), "]\n";
print "'' || 0 || 'x' -> [", ("" || 0 || "x"), "]\n";0 || 'def' -> [def]
'a' || 'b' -> [a]
1 && 2 -> [2]
0 && 2 -> [0]
'' || 0 || 'x' -> [x]None of those results is 1 or the empty string. The operators hand back one of the operands themselves:
||returns the first operand that is true, or the last operand if none is.&&returns the first operand that is false, or the last operand if none is.
So 0 || "def" is "def" because the left was false and the right was the last thing available. "a" || "b" is "a" because the left was true and evaluation stopped there. And "" || 0 || "x" walked past two false values to return "x".
This is why my $limit = $arg || 10; is a defaulting idiom: when $arg is false the expression produces 10. It is also, for exactly the same reason, why that line is wrong for a count — Chapter 3.4 §4 measured ||= replacing a real zero with its default, and the plain || form does the same thing. §6 is the fix.
5. && and and Are Not Interchangeable
Here is the section that matters. The two forms differ in precedence, and the gap straddles the assignment operator.
use strict;
use warnings;
my $a = 0 || 5;
my $b = 0 or 5;
print "my \$a = 0 || 5; -> $a\n";
print "my \$b = 0 or 5; -> $b\n";my $a = 0 || 5; -> 5
my $b = 0 or 5; -> 0Found = in conditional, should be == at precedence.pl line 5.
Useless use of a constant (5) in void context at precedence.pl line 5.$a is 5. $b is 0. The two lines look like the same statement written two ways; they are not the same statement.
The rule, stated plainly:
&& || // !bind more tightly than=. Somy $a = 0 || 5;ismy $a = (0 || 5);— the or happens first, and its result is assigned.and or not xorbind more loosely than=. Somy $b = 0 or 5;is(my $b = 0) or 5;— the assignment happens first, storing0, and theorthen operates on the assignment's result and throws5away.
Perl's own deparser confirms the grouping rather than leaving it as an assertion:
'???' unless my $b = 0;my $a = 5;The first has the assignment as the condition, which is exactly the shape §5 of Chapter 3.4 warned about. && and and behave the same way as each other here — my $x = 1 and 7; leaves $x holding 1.
When to use which
The low precedence of the word forms is a feature, not a defect. It is what makes this read correctly:
open(my $fh, "<", $path) or die "cannot open $path: $!";or binds so loosely that the whole open(...) call is its left operand without needing parentheses around it. With || the line would bind to the last argument instead and need extra parentheses to mean what you intended. That idiom is Chapter 3.9's subject.
The practical guidance is therefore:
| Situation | Use |
|---|---|
combining conditions inside an if | && || |
| assigning the result of a logical expression | && || — never the word forms |
| a trailing "do this or fail" after a statement | or |
And when in doubt, parenthesise. my $b = (0 or 5); is 5, because the parentheses settle the grouping regardless of precedence — the recommendation Chapter 3.1 made and Chapter 3.12 will make properly.
6. // Asks a Different Question
|| tests truth. // tests definedness. On a real zero they disagree:
use strict;
use warnings;
use v5.10;
my $zero = 0;
my $missing;
print "0 || 99 -> ", ($zero || 99), "\n";
print "0 // 99 -> ", ($zero // 99), "\n";
print "undef || 99 -> ", ($missing || 99), "\n";
print "undef // 99 -> ", ($missing // 99), "\n";0 || 99 -> 99
0 // 99 -> 0
undef || 99 -> 99
undef // 99 -> 99The two agree on undef and disagree on 0, which is Chapter 3.4 §4's measurement in the non-assignment form. For a count, a violation total or any number where zero is a real result, // is the operator that preserves your data.
As Chapter 3.4 established, // requires Perl 5.10 or newer, which is why the example declares use v5.10;. There is no word form of //.
7. Why This Matters in Engineering Work
The precedence bug is invisible in review. my $limit = $arg or 10; reads as a defaulting line and is not one. It assigns $arg unconditionally and discards the 10. Every token is correct, the intent is obvious to a reader, and the behaviour is wrong — and the only reliable protection is knowing that assignment plus a word form is never the combination you want.
Short-circuiting is how guarded code gets written. $total && $passed / $total avoids Chapter 3.2 §3's fatal division by zero, because when $total is 0 the division is never evaluated. That is a genuine use of the operator as flow control, and it depends on the guarantee in §3 rather than on luck.
The operand-valued return is a defaulting tool with a sharp edge. $name || "unknown" is fine, because an empty name and a missing name deserve the same treatment. $errors || "unknown" is a defect, because zero errors is the result you wanted to report. The operator does not know the difference; you do.
8. Common Mistakes
Assigning the result of a word form. my $x = $a or $b; assigns $a. Use ||.
Assuming and and && are spellings of one operator. They differ in precedence, and the difference straddles =.
Using || to default a number. A real 0 is replaced. Use //.
Expecting a logical operator to return true or false. It returns one of its operands. 1 && 2 is 2.
Forgetting that the right operand may never run. That is usually the point, but it means a side effect placed there is conditional.
Relying on the warning. Useless use of a constant appears when the discarded operand is a literal. With a variable you may get nothing.
Reaching for || in an open ... or die line. The precedence is wrong for it without extra parentheses — Chapter 3.9 covers why.
9. Exercise
Exercise 1 — the two results. Run the §5 script, record both values and both warnings. Then state which of the two lines assigned the value its author intended.
Exercise 2 — prove the short circuit. Write a subroutine that prints when called, and use it as the right operand of && and of || with both a true and a false left operand, as in §3. Record which of the four printed, and state the two rules that predicted it.
Exercise 3 — operand, not boolean. Predict the value of each before running: 5 || 9, 0 || 9, 5 && 9, 0 && 9, "" || "fallback". Then run them and state the two rules for what || and && return.
Exercise 4 — the defaulting bug. Write my $errors = 0; then my $report = $errors || "not measured"; and print $report. Then do the same with //. State which line you would commit for an error count and why in one clause.
Exercise 5 — guard a division. Using &&, write a single line that prints the pass rate when $total is non-zero and prints nothing when it is zero. Test it with $total = 0 and confirm the script does not die — contrast that with Chapter 3.2 §3.
Exercise 6 — fix the precedence. Given the broken line my $limit = $arg or 10;, give two different corrected versions: one changing the operator, one adding parentheses. Confirm both behave the same when $arg is undef.
10. Summary
- Perl has symbol and word forms of the same logic, and they differ in precedence.
&& || // !bind tighter than=;and or not xorbind looser. Somy $b = 0 or 5;is(my $b = 0) or 5and leaves$bholding0.- Never use a word form when assigning the result of a logical expression.
- The word forms exist for the trailing
or dieshape, where their loose binding takes a whole statement as the left operand. &&and||short-circuit, which is observable through side effects and makes the right operand conditional code.- They return an operand, not a boolean.
||gives the first true one,&&the first false one, otherwise the last. - Perl's true is
1and its false is the empty string. //tests definedness rather than truth, so it preserves a real0. It needs Perl 5.10+.
Chapter 3.9 takes the short-circuit property and the loose binding of or and puts them together into the idiom you will see in the first line of almost every Perl script that opens a file.
Where this fits
Part of the Perl curriculum.
