Perl “do { … } if …” as expression

Viewed 850

my $x = do { 3; } if 1; say $x # works

my $x = (do { 3; } if 1); say $x # syntax error

How come? If the do block is an expression, why can't it be parenthesised? If it's not, how does the first one parse?

4 Answers

A compound statement used for flow control (if BLOCK), as well as one with the statement modifier (used here, the postfix if), cannot appear inside parenthesis.

This restriction makes sense since such a statement may or may not return a value

if executes the statement once if and only if the condition is true.

(original emphasis)


A side note. The first example runs without warnings but it has undefined behavior, what must be avoided. From the end of the section Statement Modifiers in perlsyn

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.

(original emphasis)

Any instances of this should be rewritten, and Perl::Critic has a policy for it, making it easier to find them

It's not that the do that's the problem, it's the postfix if. That postfix can't appear inside the parens:

$ perl -E 'my $x = ( 1 if 1); say $x'
syntax error at -e line 1, near "1 if"
Execution of -e aborted due to compilation errors.

Instead, you can use the conditional operator ?: with a do in one of the branches:

$ perl -E 'my $x = ( time % 2 ? do { 1 } : () ); say $x'
my $x = do { 3; } if 1;

is actually equivalent to

( my $x = do { 3; } ) if 1;

Note that you shouldn't execute my conditionally. (More precisely, you shouldn't use a my variable that hasn't been executed. Your code is technically ok since the my is always executed before $x is used.)


An expression (my $x = do { ... }) modified by a statement modifier (if 1) is a statement.

The inside of parens must be an expression, not a statement.

You can't do

  • ( $x = 3; )
  • ( sub f { } )
  • ( if (f()) { g() } )
  • ( g() if f(); )
  • ( g() if f() )

You get the idea.

if 1 is a statement modifier. my $x = do { 3; } is a statement; do { 3; } is an expression.

Related