Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

local and statement modifiers (e.g. postfix if)

Tags:

perl

perlsyn, "Statement-Modifiers" section clearly states that

The behaviour of a my, state, or our modified with a statement modifier conditional or loop construct (for example, my $x if ... ) is undefined.

Unfortunately this list is missing local and its own documentation doesn't cover its behavior either. I assume it is undefined as well and docs are just incomplete in those particular sections.

Is this actually covered in docs anywhere?

like image 589
Oleg V. Volkov Avatar asked Mar 29 '18 22:03

Oleg V. Volkov


1 Answers

The undefined behavior of using a statement modifier on my & Co., such as

my $x = 1  if $flag;  # UNDEFINED behavior

is due to the fact that the my $x declaration happens at compile time while the = 1 assignment happens at runtime, as does the test. Thus the statement as a whole is broken (is it supposed to not even assign at all?), and the behavior of this line of code is considered undefined.

However, local is very different. From perlsub (my emphasis)

A local modifies its listed variables to be "local" to the enclosing block, eval, or do FILE --and to any subroutine called from within that block. A local just gives temporary values to global (meaning package) variables. It does not create a local variable.

Thus local does something completely different than my or our; it saves the value of its target (global) variable for the rest of the block and restores it on exit from the block.

Also, since local is a run-time operator there are no compilation-vs-runtime issues of declare + assign, as in my $x = 1, so a postfix condition can be used. Consider

use warnings;
use strict;
use feature 'say';

my $flag = shift // 1;

our $var = 1;

{
    local $var if $flag;
    $var = 2;
    say $var;
}

say $var;

When run with $flag set (script.pl) the local-ization happens and the last print shows that the global our $var has been preserved. With flag unset (script.pl 0) this doesn't happen, the value of the global is not saved away for the block, and ends up overwritten.

With local $var = 2 if $flag; neither the localization nor the assignment happens.

If only the local statement itself is conditioned that must be done in the postfix manner since the effect of local lasts only within the enclosing scope (so if ($f) { local $v } does nothing for the rest of the code).

Doing this may change the code behavior radically, and possibly at a high level, based on a mere value of a single condition; I'd recommend to thread carefully with such code. This short program in particular only shows how a postfix condition can be used. Thanks to ysth and ikegami for comments.

like image 96
zdim Avatar answered Nov 11 '22 14:11

zdim