Skip to content
VLSI Mentor

Perl Part I · Module 3 · Operators and Expressions

Precedence and Saying What You Mean

This chapter introduces no new operator. Its job is to hand you the single table that explains every grouping surprise in this module — why minus two squared is negative four, why an open with a double-pipe never reaches its die, why assigning the result of an or-expression stores the wrong value — and then to make the case that you should not rely on remembering it. Four rows account for almost every real defect, and the honest advice for everything else is to write the parentheses. Not because precedence is hard, but because a reader reviewing your diff at the end of a long day should not have to consult a table to know what your line computes.

Foundation14 min readPerlPart IFundamentalsPrecedenceReadabilityOperators

Part I · Module 3 · Chapter 3.12 · Precedence and Saying What You Mean

1. Two Rules, Then the Table

Every surprise in this module came from one of two questions.

Precedence — which operator is applied first when two different operators meet. 2 + 3 * 4 is 14 rather than 20 because * has higher precedence than +.

Associativity — which end to start from when two operators of the same precedence meet. Chapter 3.1 showed 18 / 20 * 100 grouping left to right; Chapter 3.2 showed ** grouping right to left.

That is the entire theory. The rest is a lookup.

2. The Table

This is quoted from perldoc perlop, which is where it lives and where you should go to check it. Highest precedence at the top; the left column is the associativity.

Azvya Education Pvt. Ltd.VLSI Mentor
From perldoc perlop — operator precedence and associativity
left        terms and list operators (leftward)
left        ->
nonassoc    ++ --
right       **
right       ! ~ ~. \ and unary + and -
left        =~ !~
left        * / % x
left        + - .
left        << >>
nonassoc    named unary operators
nonassoc    isa
chained     < > <= >= lt gt le ge
chain/na    == != eq ne <=> cmp ~~
left        & &.
left        | |. ^ ^.
left        &&
left        || //
nonassoc    ..  ...
right       ?:
right       = += -= *= etc. goto last next redo dump
left        , =>
nonassoc    list operators (rightward)
right       not
left        and
left        or xor

Do not try to memorise it. Read it once now so the shape is familiar, then use §3 and §4, which are the parts that actually matter.

Chapter 1.14 covered reaching for perldoc; this table is one of the best reasons to have that habit. perldoc perlop is two seconds away and is always right about the Perl you are running.

3. Every Surprise in This Module Is a Row in That Table

Here is the payoff. Each thing that caught you in this module is one line of the table.

** sits above the unary minus

Azvya Education Pvt. Ltd.VLSI Mentor
The two rows
right       **
right       ! ~ ~. \ and unary + and -

** is higher, so it binds first. That is why Chapter 3.2 measured -2 ** 2 as -4 — Perl read it as -(2 ** 2). And ** is marked right, which is why 2 ** 3 ** 2 is 512 rather than 64.

*, /, % and x share one row

Azvya Education Pvt. Ltd.VLSI Mentor
One row, left associative
left        * / % x

Same precedence, grouped left to right — which is Chapter 3.1's 18 / 20 * 100 giving 90. It also tells you that x binds as tightly as multiplication, so "ab" x 2 . "c" repeats before it joins:

Azvya Education Pvt. Ltd.VLSI Mentor
x-before-dot.pl
use strict;
use warnings;

print "ab" x 2 . "c", "\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
ababc

ababc, not abcabc. x is on the row above ., so "ab" x 2 happened first.

|| is above the comma; or is below it

Azvya Education Pvt. Ltd.VLSI Mentor
Three rows, in order
left        || //
...
left        , =>
...
left        or xor

This is the single most valuable thing the table tells you, and it explains Chapter 3.9's measured defect exactly.

|| is above the comma row, so inside open my $fh, "<", $path || die ... it binds tighter than the commas and attaches to $path alone. or is below the comma row, so it binds looser than the whole argument list and takes the entire open ... call as its left operand. That is why one form works and the other silently exits zero.

= is above the comma, which is above and/or

Azvya Education Pvt. Ltd.VLSI Mentor
Three rows, in order
right       = += -= *= etc. goto last next redo dump
left        , =>
...
left        and
left        or xor

= is above or, so my $b = 0 or 5; groups as (my $b = 0) or 5 — Chapter 3.8's measurement. And || is above =, so my $a = 0 || 5; groups as my $a = (0 || 5) and works.

That is the whole of the and-versus-&& problem, in two rows.

4. The Four Rows That Bite

If you retain nothing else from the table, retain these.

RememberBecause
** is above unary minus, and is right associative-2 ** 2 is -4; 2 ** 3 ** 2 is 512
&& || // are above =my $x = $a || $b; works as intended
and or not are below =my $x = $a or $b; assigns $a and discards $b
|| is above the comma; or is below itopen(...) or die works; open ... || die does not

Everything else, parenthesise.

5. The Case for Parentheses You Do Not Need

($passed / $total) * 100 has exactly the same meaning as $passed / $total * 100. The parentheses are redundant. Write them anyway, and here is the argument.

The reader's cost is not symmetric with yours. You know what you meant when you wrote the line. A reviewer, or you in four months, has to either recall Perl's associativity or decide it is probably fine. The second is what happens at the end of a long review, and it is how Chapter 3.1's misgrouped percentage ships.

Redundant parentheses cost nothing at runtime. There is no performance argument against them.

They survive editing. When someone inserts a term into an expression, explicit grouping constrains where it can go. Implicit grouping silently re-associates around the new operator.

They mark the places you thought about. A line with deliberate parentheses tells a reader that the author considered the order. A line without them is ambiguous about whether the order was considered or merely survived.

The one caution is that parentheses can be overdone. ((($a))) and parenthesising every single term makes a line harder to read, not easier. The target is one level of grouping that makes the intended order visible:

Azvya Education Pvt. Ltd.VLSI Mentor
Three versions of one line
my $rate = $passed / $total * 100;        # correct, relies on the reader
my $rate = ($passed / $total) * 100;      # correct, and says so  <-- prefer
my $rate = (($passed) / ($total)) * 100;  # correct, and noisy

6. When Parentheses Are Not Enough

Two cases from this module where the fix is not a parenthesis.

A line that needs splitting. If an expression needs three levels of grouping to be readable, the real problem is that it is doing too much. Name an intermediate value:

Azvya Education Pvt. Ltd.VLSI Mentor
Before and after
my $ok = $count == $expected && $status eq "DONE" && $slack > 0;

my $counts_match = $count == $expected;
my $finished     = $status eq "DONE";
my $timing_clean = $slack > 0;
my $ok = $counts_match && $finished && $timing_clean;

The second version costs three lines and makes a failure diagnosable — you can print any of the three names to find out which condition was false. The first tells you only that the conjunction was false.

A print followed directly by a parenthesis. Chapter 3.2 §5 measured this: print (2 ** 3) ** 2; prints 8, because the parenthesis became print's argument list rather than a grouping. Adding more parentheses in the wrong place does not help; print +(2 ** 3) ** 2; or print ((2 ** 3) ** 2); is the fix, and use warnings reports the problem as print (...) interpreted as function.

That case is a useful reminder that a parenthesis immediately after a function name means arguments, everywhere in Perl.

7. Why This Matters in Engineering Work

Precedence bugs are the hardest class to catch in review because every token is correct. There is no misspelled variable, no wrong operator, no missing semicolon — just a grouping that differs from the author's intention. Reviewers check for things that look wrong, and these do not.

Two of them in this module exit zero. open ... || die reports success after failing to read its input, and my $limit = $arg or 10 assigns silently. A flow that reads exit status learns nothing in either case.

The cheap defence is a habit, not knowledge. Writing ($a / $b) * 100 does not require remembering which row / is on. It requires only the habit of grouping what you meant, which is why this chapter ends on the habit rather than on the table.

8. Common Mistakes

Trusting memory over perldoc perlop. The table is two seconds away and authoritative for your Perl.

Assuming and is && with nicer spelling. They are on different rows, either side of =.

Assuming ** groups left to right. It is the one right-associative arithmetic operator.

Forgetting that unary minus is weaker than **. -2 ** 2 is -4.

Writing print ( with a space and expecting grouping. The parenthesis becomes the argument list.

Attributing Chapter 3.10's x behaviour to precedence. The parentheses there change the operand's kind, not the grouping.

Removing redundant parentheses to tidy a line. They were load-bearing for the reader even when not for Perl.

9. Exercise

Exercise 1 — predict from the table. Without running them, use the table in §2 to predict each: 2 + 3 * 4, -3 ** 2, 2 ** 2 ** 3, 10 - 4 - 3, "ab" x 2 . "c". Then run all five and record any you got wrong, naming the row that decided it.

Exercise 2 — the two or-forms. Write my $x = 0 || 5; and my $y = 0 or 5; in one script, print both, and capture STDERR. Then point at the two rows in §2 that explain the difference.

Exercise 3 — the open idiom. Write both open(my $fh, "<", "/no/such") or die "a: $!\n"; and the broken open my $fh2, "<", "/no/such" || die "b: $!\n"; as separate scripts. Record the exit status of each and explain, using the comma row, why only one died.

Exercise 4 — parenthesise for a reviewer. Take my $util = $used / $total * 100; and my $ok = $a == $b && $c ne $d; and add the minimum parentheses that make the intended order visible without noise. Justify each pair you added in one clause.

Exercise 5 — split instead. Rewrite my $pass = $errors == 0 && $warnings < $limit && $status eq "DONE"; as named intermediate values, then add one line that reports which condition failed. State what that version gives you that the original does not.

Exercise 6 — look it up. Run perldoc perlop and find the precedence table. Report where ?: sits relative to =, and say what that means for my $x = $c ? 1 : 2;.

10. Summary

  • Precedence decides which of two different operators applies first; associativity decides the order for two of the same precedence.
  • The table in perldoc perlop is authoritative for the Perl you are running. Look it up rather than recalling it.
  • Every grouping surprise in this module is one row of it — ** above unary minus and right associative, * / % x sharing a left-associative row, and the ||/=/,/or ordering.
  • Four rows cover almost every real defect, and §4 lists them.
  • ("ab") x 3 is not a precedence case — those parentheses change the operand from a string to a list.
  • Write the redundant parentheses. They cost nothing, survive editing, and spare the reader a lookup.
  • When one level of grouping is not enough, name intermediate values instead of adding a third level — and you gain a diagnosable failure.
  • A parenthesis straight after a function name is an argument list, which is why print (...) behaves as it does.

That closes Module 3. You now have every operator family in Perl: arithmetic, string, assignment, both comparison families, the three-way comparisons, logic with its short-circuit behaviour, ranges and repetition, bitwise, and the rules that decide what order they apply in. Module 4 turns to the functions that work on strings, where these operators stop being the whole toolkit and start being the glue between calls.

Where this fits

Part of the Perl curriculum.