Home / Alt manpages / perlsyn(1)

  • perlsyn(1)
  • User command
  • linux

Write Predictable Perl Control Flow with perlsyn

You will finish with a small set of Perl control-flow patterns that compile locally and make their scope, loop behaviour and conditions visible. The examples use Perl v5.38.2, as reported by the installed perlsyn(1) manual page dated 14 September 2026. Allow about 20 minutes if you are reading the syntax as you go.

You need a shell and the installed perl interpreter. Everything here runs as an ordinary user. It creates only temporary files under /tmp if you choose to save a script. No elevated privileges are needed.

1. Start with a statement and a block

Perl is free-form, so indentation helps people but does not define the program. A simple statement is an expression evaluated for its side effect, and it normally ends with a semicolon. A final statement in a block may omit that semicolon, but keeping it makes later edits less surprising.

use strict;
use warnings;

my $name = 'Ada';
print "Hello, $name\n";

Save that as /tmp/perlsyn-demo.pl, then check its syntax without running it:

$ perl -c /tmp/perlsyn-demo.pl
/tmp/perlsyn-demo.pl syntax OK

perl -c compiles the file and stops before executing it. This is a useful checkpoint after changing braces, declarations or loop headers. If you see an error, fix the first reported line before investigating later messages.

2. Make conditional scope explicit

Compound conditionals operate on blocks, so the braces are part of the syntax. This avoids the dangling-else ambiguity found in some C-like languages:

my $temperature = 7;

if ($temperature < 0) {
    print "freezing\n";
} elsif ($temperature < 10) {
    print "cold\n";
} else {
    print "mild\n";
}

Run it to check the result:

$ perl /tmp/perlsyn-demo.pl
cold

Use unless only when its negative condition is clearer. It can have else and elsif, but several negative branches force readers to mentally reverse each test. A normal if often communicates the same rule more directly.

Declarations have a confusing timing rule. Subroutine declarations take effect at compile time, while lexical variable declarations such as my also have a run-time effect as the statement sequence is evaluated. Keep declarations near the start of a scope unless there is a clear reason not to. A name declared inside a block is not available after that block.

3. Use postfix modifiers for one short action

A simple statement may have one modifier immediately before its terminating semicolon. This is compact and useful when the action and condition fit on one readable line:

print "backup needed\n" if $needs_backup;
warn "not ready\n" unless $ready;

The modifier changes when the statement runs, not its position in the source. The same form supports while, until, for and foreach. For example, this prints each item with the default topic variable $_:

print "item: $_\n" for qw(alpha beta gamma);

Do not hide a multi-step operation behind a long postfix line. Use a block when readers need to see several actions or when loop control such as next and last matters. A postfix do block runs once before its while or until condition is tested, which is useful for reading input but easy to miss during maintenance.

4. Choose the loop form deliberately

Use a C-style loop when you need an explicit initialiser, condition and increment. Use for or its synonym foreach when visiting values:

for my $number (2, 4, 6) {
    print "$number\n";
}

When no iterator variable is supplied, Perl uses $_. With an iterator variable, the variable is an alias to each list item rather than an unrelated copy. That means this deliberately changes the array:

my @numbers = (1, 2, 3);
for my $number (@numbers) {
    $number *= 10;
}
print "@numbers\n";
$ perl /tmp/perlsyn-demo.pl
10 20 30

That aliasing is convenient for in-place edits, but it is a common distraction when the loop was intended only to inspect values. Use a separate calculated value when mutation is not part of the job. Do not add or remove elements from an array while a foreach loop is traversing it; the manual warns that this can confuse the iteration.

5. Control nested loops with labels

next skips to the next iteration, redo repeats the current iteration without retesting the condition, and last leaves a loop. Without a label, each applies to the innermost enclosing loop. Label the loop when the intended target is not obvious:

OUTER: for my $left (1, 2) {
    for my $right (1, 2, 3) {
        next OUTER if $left == $right;
        print "$left:$right\n";
    }
}

Here next OUTER skips the rest of the inner loop and starts the next outer iteration. A label is also a useful guard against future nesting changes. Do not rely on a loop's return value: perlsyn describes it as unspecified and subject to change.

6. Avoid the modifier declaration trap

The installed manual explicitly warns that my, state or our combined with a conditional or loop modifier has undefined behaviour. Do not write this:

my $value if $condition;

Depending on the version and execution history, $value may be undefined, retain an earlier value or behave differently in a future release. Declare first, then assign in an ordinary block:

my $value;
if ($condition) {
    $value = 'set';
}

Run perl -c again after moving declarations or braces. Compilation success proves that the syntax is accepted; it does not prove that a condition is logically correct, so run a small test with representative values too.

Done means

  • Your script passes perl -c PATH before you run it.
  • Conditionals use visible blocks when they contain more than one action.
  • Postfix modifiers are reserved for short, readable statements.
  • You know that a foreach iterator can alias and change list elements.
  • Nested loop exits name their target when the inner loop is not the intended one.
  • No my, state or our declaration relies on a statement modifier.