Skip to content
VLSI Mentor

Perl Part I · Module 8 · Conditional Statements

Statement Modifiers: Conditions After the Action

Perl lets you put the condition at the end of a statement instead of wrapping the statement in a block. This is the form you will read most often in real scripts, because the dominant use of a conditional inside a loop is a guard that skips a line, and a guard written this way takes one line instead of three. There are five modifiers, not two, and three restrictions worth knowing before you lean on them: the statement cannot be a block, two modifiers cannot be stacked, and one specific combination — declaring a variable with a modifier attached — compiles silently, runs, produces the answer you hoped for, and is documented by Perl's own reference as undefined behaviour.

Foundation16 min readPerlPart IControl FlowConditionals

Part I · Module 8 · Chapter 8.3 · Statement Modifiers: Conditions After the Action

1. Five Modifiers, Not Two

Chapter 3.9 introduced the form and the or die idiom built on it. The complete set is wider than most people realise.

Azvya Education Pvt. Ltd.VLSI Mentor
modifiers.pl
use strict;
use warnings;

my $n = 5;

print "if fired\n"     if $n > 3;
print "unless fired\n" unless $n > 9;

my $i = 0;
print "while $i\n" while $i++ < 2;

my $j = 0;
print "until $j\n" until ++$j > 2;

print "for $_\n" for 1 .. 2;
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
if fired
unless fired
while 1
while 2
until 1
until 2
for 1
for 2
ModifierRuns the statement
EXPR if COND;once, when COND is true
EXPR unless COND;once, when COND is false
EXPR while COND;repeatedly, while COND is true
EXPR until COND;repeatedly, until COND becomes true
EXPR for LIST;once per item, with $_ set

foreach is accepted as a synonym for for in the modifier position, exactly as it is for the block form.

The for modifier is the most useful of the three loop forms:

Azvya Education Pvt. Ltd.VLSI Mentor
The idiom you will actually write
print "$_\n" for sort keys %report;
$total += $_->{paths} for @rows;

$_ is the implicit loop variable — Module 9 covers it properly, and here it is simply what for sets. The second line is an accumulation in one statement, which is a genuinely good use because there is nothing worth naming.

2. Restriction One: No Block

Azvya Education Pvt. Ltd.VLSI Mentor
modblock.pl
use strict;
use warnings;

{ print "a\n" } if 1;
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output — STDERR
syntax error at modblock.pl line 4, near "if 1"
Execution of modblock.pl aborted due to compilation errors.
Azvya Education Pvt. Ltd.VLSI Mentor
Measured exit status
255

A modifier attaches to a single statement, and a block is not a statement. There is no way to write a two-statement body in the postfix form — the moment you need two things done conditionally, you need the block form from Chapter 8.1.

This is a useful limit rather than an annoyance. It means a postfix guard is always exactly one action, which is why the form stays readable: you never have to scan for where the conditional body ends.

3. Restriction Two: No Stacking

Azvya Education Pvt. Ltd.VLSI Mentor
modstack.pl
use strict;
use warnings;

my $n = 5;

print "both\n" if $n > 3 unless $n > 9;
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output — STDERR
syntax error at modstack.pl line 6, near "3 unless"
Execution of modstack.pl aborted due to compilation errors.
Azvya Education Pvt. Ltd.VLSI Mentor
Measured exit status
255

One modifier per statement. Combine the conditions instead:

Azvya Education Pvt. Ltd.VLSI Mentor
The fix
print "both\n" if $n > 3 && $n <= 9;

The same applies to mixing kinds — you cannot write print $_ for @list if $flag;. Put the guard outside in a block, or fold it into one condition.

4. Restriction Three: my With a Modifier Is Undefined

This one does not fail. That is the problem.

Azvya Education Pvt. Ltd.VLSI Mentor
myif.pl
use strict;
use warnings;

my $x = 5 if 1;

print "x = ", (defined $x ? $x : "undef"), "\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output — STDOUT
x = 5
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
Azvya Education Pvt. Ltd.VLSI Mentor
Measured exit status
0

It compiled, ran, printed the value you would hope for, and warned about nothing. Both use strict and use warnings were on.

And Perl's own reference says not to do it. From perldoc perlsyn, captured verbatim:

Azvya Education Pvt. Ltd.VLSI Mentor
perldoc perlsyn — captured
NOTE: The behaviour of a "my", "state", or "our" modified with a
statement modifier conditional or loop construct (for example, "my $x if
...") is undefined. The value of the "my" variable may be "undef", any
previously assigned value, or possibly anything else. Don't rely on it.
Future versions of perl might do something different from the version of
perl you try it out on. Here be dragons.

5. The Dominant Real Use: next and last

Most statement modifiers you read are attached to a loop-control keyword.

Azvya Education Pvt. Ltd.VLSI Mentor
loopmod.pl
use strict;
use warnings;

for my $n (1 .. 6) {
    next if $n % 2;
    last if $n > 4;
    print "$n ";
}
print "\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
2 4

1, 3 and 5 were skipped by next if $n % 2 — an odd number leaves a remainder of 1, which is true. 6 ended the loop via last if $n > 4, so 6 never printed. Module 9 owns next and last; what matters here is the shape, because it opens almost every parsing loop:

Azvya Education Pvt. Ltd.VLSI Mentor
The guard stack
while (my $line = <$fh>) {
    next if $line =~ /^\s*$/;      # blank
    next if $line =~ /^\s*#/;      # comment
    chomp $line;
    ...
}

Each guard is one line and reads as a sentence. The alternative is nesting, and §6 measures the difference rather than asserting it.

6. Where Postfix Stops Helping

Both forms produce the same answer, so the choice is readability — which is worth demonstrating on identical input rather than arguing about.

Azvya Education Pvt. Ltd.VLSI Mentor
readability.pl — four postfix guards
use strict;
use warnings;

my @rows = (
    "alu  -0.12",
    "",
    "# a comment line",
    "fifo  0.34",
    "regf",
);

my $kept = 0;

for my $line (@rows) {
    next if $line =~ /^\s*$/;
    next if $line =~ /^\s*#/;
    my ($blk, $slack) = split ' ', $line;
    next unless defined $slack;
    $kept++;
    printf "  %-5s %+.2f\n", $blk, $slack;
}

printf "kept %d, skipped %d of %d\n", $kept, scalar(@rows) - $kept, scalar(@rows);
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
  alu   -0.12
  fifo  +0.34
kept 2, skipped 3 of 5

The same logic nested:

Azvya Education Pvt. Ltd.VLSI Mentor
nested.pl — the same filter, nested
for my $line (@rows) {
    if ($line !~ /^\s*$/) {
        if ($line !~ /^\s*#/) {
            my ($blk, $slack) = split ' ', $line;
            if (defined $slack) {
                $kept++;
                printf "  %-5s %+.2f\n", $blk, $slack;
            }
        }
    }
}
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output — byte-identical, verified with diff
  alu   -0.12
  fifo  +0.34
kept 2, skipped 3 of 5

Identical output, confirmed by diff rather than by reading. The postfix version reads top to bottom as a list of reasons to skip a line, with the real work at the left margin. The nested version pushes the real work four levels deep and makes the reader match closing braces to work out which condition guards what.

That is the case for postfix: it is flat. And that is also its limit:

Postfix is clearer whenBlock form is clearer when
the action is one short statementthe body has two or more statements
the condition is shortthe condition needs && and ||
it is a guard — next, last, return, diethere is an else
the action is the point, the condition a footnotethe condition is the point

7. do { } while Is Not a Statement Modifier

This looks like the while modifier and behaves differently, which earns one measurement.

Azvya Education Pvt. Ltd.VLSI Mentor
dowhile.pl
use strict;
use warnings;

my $i = 10;

do { print "ran once at $i\n" } while ($i < 5);

my $k = 10;
while ($k < 5) { print "never\n" }
print "the plain while ran zero times\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
ran once at 10
the plain while ran zero times

The do block ran even though the condition was false from the start. The plain while loop with the same condition ran zero times.

The reason is that this is not the modifier form. It is a do BLOCK, which is an expression, with a while modifier attached — and because a modifier runs its statement and then tests, the block executes once before the condition is evaluated at all. §2 established that a bare block cannot take a modifier; do { } can, precisely because it is an expression rather than a block statement.

Two consequences:

  • do { } while is the test-at-the-bottom loop, Perl's equivalent of C's do ... while. Use it when the body must run at least once — reading until a sentinel, retrying an operation.
  • next and last do not work inside it, because it is not a real loop. That is a genuine trap; the fix is a proper while loop with the first iteration handled explicitly.

8. Why This Matters in Engineering Work

It is the shape of every parsing loop. Skip blank lines, skip comments, skip rows missing a field, then do the work. Written postfix those guards are four flat lines; written nested they are four levels of indentation with the real work at the bottom — and §6 confirmed the outputs are identical, so the only thing you are choosing is whether the next person can read it.

or die is this same mechanism. Chapter 3.9 owns the idiom, including why it must be or rather than || — a precedence trap where the script silently never dies and exits 0. open ... or die is the single most common statement modifier in Perl, so that chapter is worth rereading alongside this one.

And the my case is a specification hazard rather than a bug you can test for. It works today on this Perl, both attempts to break it came out clean, and perlsyn still says the value may be anything. Splitting it into two lines costs nothing and removes the dependency entirely.

9. Common Mistakes

Attaching a modifier to a block. { ... } if COND; is a syntax error, exit 255. Use the block form.

Stacking two modifiers. One per statement; combine the conditions with &&.

my $x = ... if COND; Undefined behaviour per perlsyn, and it will not warn. Declare first, assign second.

Expecting next/last to work inside do { } while. It is not a loop.

Expecting do { } while to test before the first run. It always runs the body once.

A postfix guard that wraps onto a second line. It has lost its advantage; use the block form.

|| die instead of or die. A precedence trap — Chapter 3.9 measured that it exits 0 and never dies.

10. Exercise

Exercise 1 — all five. Write one statement for each modifier — if, unless, while, until, for — and confirm you get the eight lines from §1. Then rewrite the for one as foreach and confirm the output is unchanged.

Exercise 2 — the accumulation idiom. Build an array of three hash references with a numeric field. Total that field with $total += $_->{field} for @rows;. Then print every key of a hash with print "$_\n" for sort keys %h;.

Exercise 3 — no block. Write { print "a\n" } if 1; and record the exact error and exit status. Then write the same thing two ways that do work: as a block if, and as a single postfix statement.

Exercise 4 — no stacking. Write print "both\n" if $n > 3 unless $n > 9; and record the error. Then write one combined condition that produces the intended behaviour, and test it with $n of 2, 5 and 12.

Exercise 5 — the undefined one. Write my $x = 5 if 1; and record STDOUT, STDERR and the exit status. Then put the same construct in a subroutine, call it five times alternating a true and false argument, and record every result. Write two sentences: one on what you observed, one on why observing it proves nothing.

Exercise 6 — the fix. Rewrite Exercise 5's subroutine with the declaration and the assignment on separate lines. Confirm the results and state what is now guaranteed that was not before.

Exercise 7 — the guard stack. Take §6's five input rows and write the filter with four postfix guards. Then write it again with nested if blocks. Run both, diff the outputs, and confirm they match. Say which you would put in a shared script and give one reason.

Exercise 8 — do { } while. Run §7's example and confirm the do block runs with a false condition while the plain while does not. Then put a last inside the do block and record exactly what happens.

11. Summary

  • There are five modifiers: if, unless, while, until, and for/foreach.
  • EXPR for LIST; sets $_ per item and is the neatest way to write a one-line accumulation or dump.
  • A modifier attaches to one statement, and a block is not a statement — { ... } if COND; is a syntax error, exit 255.
  • Modifiers cannot be stacked. Combine the conditions instead.
  • my $x = ... if COND; is undefined behaviour per perlsyn. It compiled, ran, printed 5 and warned nothing on Perl 5.34.1, and two attempts to reproduce the documented misbehaviour both came out correct. Declare on one line, assign on the next.
  • The dominant real use is next if / last if inside a loop, which is why nearly every parsing loop opens with a stack of them.
  • Postfix and nested forms give identical output — verified with diff. The only question is whether the action or the condition is the point.
  • If the line wraps, use the block form.
  • do { } while is not a modifier — the body runs once before the condition is tested, and next/last do not work inside it.
  • or die is this mechanism; Chapter 3.9 owns it, including why || die silently never fires.

Chapter 8.4 moves the condition inside an expression, so a value can be chosen where it is used instead of assigned in two separate branches.

Where this fits

Part of the Perl curriculum.