Skip to content
VLSI Mentor

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

MeaningSymbolWord
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:

Azvya Education Pvt. Ltd.VLSI Mentor
true-and-false.pl
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";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
!0 is [1] with length 1
!1 is [] with length 0

Perl'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:

Azvya Education Pvt. Ltd.VLSI Mentor
short-circuit.pl
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");
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
false && noisy:
true || noisy:
true && noisy:
  evaluated: right of && when left true
false || noisy:
  evaluated: right of || when left false

The 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:

Azvya Education Pvt. Ltd.VLSI Mentor
returns-an-operand.pl
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";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
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.

Azvya Education Pvt. Ltd.VLSI Mentor
precedence.pl
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";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output — STDOUT
my $a = 0 || 5;  -> 5
my $b = 0 or 5;  -> 0
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output — STDERR
Found = 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 =. So my $a = 0 || 5; is my $a = (0 || 5); — the or happens first, and its result is assigned.
  • and or not xor bind more loosely than =. So my $b = 0 or 5; is (my $b = 0) or 5; — the assignment happens first, storing 0, and the or then operates on the assignment's result and throws 5 away.

Perl's own deparser confirms the grouping rather than leaving it as an assertion:

Azvya Education Pvt. Ltd.VLSI Mentor
perl -MO=Deparse -e 'my $b = 0 or 5;'
'???' unless my $b = 0;
Azvya Education Pvt. Ltd.VLSI Mentor
perl -MO=Deparse -e 'my $a = 0 || 5;'
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:

Azvya Education Pvt. Ltd.VLSI Mentor
The idiom the word forms exist for
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:

SituationUse
combining conditions inside an if&& ||
assigning the result of a logical expression&& || — never the word forms
a trailing "do this or fail" after a statementor

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:

Azvya Education Pvt. Ltd.VLSI Mentor
defined-or.pl
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";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
0     || 99 -> 99
0     // 99 -> 0
undef || 99 -> 99
undef // 99 -> 99

The 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 xor bind looser. So my $b = 0 or 5; is (my $b = 0) or 5 and leaves $b holding 0.
  • Never use a word form when assigning the result of a logical expression.
  • The word forms exist for the trailing or die shape, 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 1 and its false is the empty string.
  • // tests definedness rather than truth, so it preserves a real 0. 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.