Operators#

Perl spells the boolean operators twice, in two different precedence classes. The C-style symbolic forms (&&, ||, !) bind tightly; the word forms (and, or, not) bind very loosely. There is also // for the defined-or idiom, xor for exclusive-or, and the ternary ?: which is logical selection in disguise.

The full inventory#

Operator

Reads as

Returns

Precedence

!

not

normalised 1 or empty string

high

&&

and

left if false, else right (the operand)

high

||

or

left if true, else right (the operand)

high

//

defined-or

left if defined, else right (the operand)

high

xor

exclusive or

normalised 1 or empty string

very low

not

not

normalised 1 or empty string

very low

and

and

left if false, else right (the operand)

very low

or

or

left if true, else right (the operand)

very low

?:

ternary

middle if condition true, else right (operand)

low

Two things in this table do most of the work:

  • && and || return an operand, not a boolean. This is the property that makes Perl’s idioms work the way they do. See below.

  • The symbolic and word forms differ in precedence, not meaning. && and and compute the same logical function. They differ in how tightly they bind to other operators around them. That is why you see or die after function calls but || inside expressions — the chapter on the or die idiom explains.

Short-circuit and the operand-return rule#

&& and || evaluate left-to-right and stop as soon as the result is determined.

  • A && B — if A is false, return A without evaluating B. Otherwise return B.

  • A || B — if A is true, return A without evaluating B. Otherwise return B.

Crucially, the return value is the operand itself, not a normalised true/false. This is what makes patterns like $config{port} || 8080 work: if $config{port} is set (truthy), the expression is $config{port}’s value; if not, it is 8080.

my $port = $config{port} || 8080;
my $name = $user_input  || "anonymous";
my $list = shift        || [];

The same property powers or die:

open my $fh, '<', $path  or die "open $path: $!";
#  ^^^^^^^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^^^^^^^
#  if open returns true,  the right-hand operand
#  the whole expression   is never evaluated
#  is true and we move on

open returns true on success, undef on failure. On success the short-circuit stops on the left and die never runs. On failure the right-hand operand evaluates, which is the die call.

// — defined-or#

|| treats 0, "", and "0" the same as undef. Sometimes that is wrong: a configured 0 is a real value, not a missing one. // short-circuits on definedness instead of truthiness:

  • A // B — if A is defined, return A. Otherwise return B.

my $port    = $config{port} || 8080;   # 0 means "use default" — wrong
my $port    = $config{port} // 8080;   # 0 means "0", undef means "use default"
my $verbose = $opt{verbose} // 0;       # 0 is a real choice

The compound assignment forms ||= and //= save you from repeating the variable on the left:

$config{port} //= 8080;   # set only if currently undef
$cache{$key}  ||= compute($key);   # set if currently false

xor and the absence of a high-precedence form#

There is no ^^ in Perl. xor exists only as the very-low-precedence word form, and unlike and/or it does not short-circuit (it cannot — both sides have to be evaluated to determine the result). It returns a normalised 1 or "", not an operand.

if ($admin xor $guest) { ... }   # exactly one of the two

For xor on values rather than on truthiness, the bitwise ^ applies to integers — covered in the applications chapter.

! and not#

Both negate. ! is the symbolic high-precedence form; not is the word low-precedence form. Both return a normalised value: 1 for “the operand was false”, empty string "" for “the operand was true”.

my $missing = ! $config{port};       # 1 if port is unset/0/""/"0"
return if not @items;                # cleaner reading than `if !@items`

The ternary ?:#

COND ? X : Y is logical selection: if COND is true the value is X, otherwise Y. It is not just sugar for an if/else because it is an expression — it has a value, can be assigned, returned, passed as an argument.

my $label = $count == 1 ? "1 item" : "$count items";
return $ok ? \%result : undef;

Beware nesting: $a ? $b : $c ? $d : $e parses as $a ? $b : ($c ? $d : $e) — chained ?: is right-associative, which is usually what you want for an if/elsif/else cascade but is worth knowing rather than guessing.

Why the word forms exist: precedence, not style#

|| binds more tightly than =. or binds less tightly than = (or ,, or anything else of conventional weight). That is the entire reason the word forms exist.

my $fh = open $h, '<', $path || die "no $path: $!";
#                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^
# parses as: open $h, '<', ($path || die "no $path: $!")
# which is: open the file named "either $path or the die-message"
# which is: catastrophe.

my $fh = open $h, '<', $path or die "no $path: $!";
# parses as: ($fh = open $h, '<', $path) or die "no $path: $!"
# which does what you meant.

Rule of thumb:

  • Inside an expression, where you want the value to flow into a variable or another operator: use &&, ||, !, //.

  • After a statement, where you want a side-effect (die, warn, return) to fire conditionally: use or, and, not.

What you should remember from this chapter#

  • && and || are not predicates that return 1 or 0 — they return one of their operands. This is the foundation of ||, ||=, or die, and most short defaulting idioms.

  • // is || for definedness, not truthiness. Use it when 0 is a legitimate value.

  • The word forms exist to bind looser than assignment, not for style.

  • xor exists, has no symbolic form, does not short-circuit.