stephanm has asked for the wisdom of the Perl Monks concerning the following question:

Hi monks,
in an attempt to run a script as stand-alone that usually is called from within another script with a "do 'script'" statement I attempted to conditionally define some variables.
The two 3-liners below - the first one simulating a call from a script with $var defined - the second one the stand-alone context - emulate the situation.
Script1:
my $var = "xxx\n"; my $var="zzz\n" unless defined($var); print $var;

Script2:
#my $var = "xxx\n"; my $var="zzz\n" unless defined($var); print $var;

As a result the first script prints nothing.
When called with perl -w I get the output:
"my" variable $var masks earlier declaration in same scope at unlesste +st.pl line 2. Use of uninitialized value in print at unlesstest.pl line 3.

Both error messages make no sense to me and seem to be contradicting each other.
The output of the second script is:
Name "main::var" used only once: possible typo at unlesstest.pl line 2 +. zzz
Even so this time the line is printed the warning about an undefined variable inside a defined() context does not make a lot of sense to me.
Anyone with an explanation and possibly a solution for my problem?

Thanks

Replies are listed 'Best First'.
Re: conditional definition of variables
by FunkyMonk (Bishop) on Aug 23, 2008 at 13:52 UTC
    my $foo = 1 does stuff in two parts:
    • $foo is declared at compile time
    • $foo is initialised at run time

    That's the reason for your contradictory error messages

    Here's what perlsyn has to say about it:

    NOTE: The behaviour of a my statement modified with a statement modifier conditional or loop construct (e.g. 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.
      I see. That seems to imply that the second "my" statement is executed unconditionally.
      The conditionality of the statement only applies to the run time part.
      Makes sense now. Thanks.
Re: conditional definition of variables
by betterworld (Curate) on Aug 23, 2008 at 13:50 UTC

    "my" declares a variable. A variable should not be declared more than once in a block. Simply leave out the (second) "my":

    $var = "zzz\n" unless defined $var;

    However, if you want to print the variable, you should make sure that $var is defined (otherwise you get a warning with "use warnings;"):

    $var = defined($var) ? "zzz\n" : "";

    By the way, including another script with "do" isn't good practice. Consider making it a module (perlmod).

      Thanks, betterworld.
      Your proposal works.
      When you say a variable should not be defined more than once in a block - I did not think I did that in the code as the second declaration is conditional to the variable not being defined.
      Am I missing anything here?
        "Definition" (or declaration) of the variable happens at compile time. The conditional happens at run time.

        So, if you have

        my $var = "..." if EXPR;
        you will have all the compile time effects, and maybe the runtime effects. This was abused (with 'EXPR' being 0) to get "state" variables in 5.8 and before (which didn't always 'work'), which in turn let to the "state" keyword in 5.10.
Re: conditional definition of variables
by shmem (Chancellor) on Aug 23, 2008 at 18:07 UTC
    Script2:
    #my $var = "xxx\n"; my $var="zzz\n" unless defined($var); print $var;
    Name "main::var" used only once: possible typo at unlesstest.pl line 2 +. zzz

    That happens because before doing the assignment, the right-hand-side of the statement must be resolved. So, a global variable $var is created, and then masked immediately by a lexical variable $var. These variables are not related, and the warning is about the global one, which isn't accessible any more, since it is masked immediately by the my variable.

Re: conditional definition of variables
by rir (Vicar) on Aug 26, 2008 at 15:24 UTC
    my $var="zzz\n" unless defined($var)

    This is the problem; the behavior of a my declaration in a statement with a conditional modifier is undefined. This is documented in perldoc perlsyn. Convert that to an unless statement:

    unless ( defined $var) { my $var = "zzz\n" }
    and you will get predictable behavior, but, probably, not what you want.

    I don't know what you are trying to do, but you might consider using a hash to hold the set of variables that may or may not exist.

    Be well,
    rir

      unless ( defined $var) { my $var = "zzz\n" } and you will get predictable behavior, but, probably, not what you want.
      To expand on what you said, the OP should note that, since the my $var inside the unless has nothing to do with the my $var outside, so this code would leave the outer $var alone, whether or not it's defined. As mentioned by betterworld, just omitting the second my (whether you use the unless in modifier or BLOCK form) gives the (probably-)desired effect of modifying the outer $var.