This will seem trivial to some, but it was very, very annoying to track down in a much larger program. What behavior do you expect from the following program?

use warnings; use strict; + foo('bar'); my %hash = (bar => 'value'); sub foo { print $hash{+shift} }

The hash is clearly in scope by the time we get to &foo, but variable declaration occurs at compile while while assignment occurs at runtime. Thus, the output you're likely to see on your console is "Use of uninitialized value in print at ..."

This declaration/assignment mismatch should at least generate a warning, but I'm not sure if it's even possible for the parser to determine. Still, it wasted a fair chunk of my time today :/

Cheers,
Ovid

New address of my CGI Course.

Replies are listed 'Best First'.
Re: Mini-rant about 'strict'
by japhy (Canon) on Aug 04, 2004 at 20:39 UTC
    I've been bitten by that a couple times before. One work-around is to say:
    my %hash; BEGIN { %hash = (...); }
    It'd be nice to be able to say that all at once, like: my %hash = BEGIN { (...) }; as merlyn has asked about to the P5P.
    _____________________________________________________
    Jeff japhy Pinyan, P.L., P.M., P.O.D, X.S.: Perl, regex, and perl hacker
    How can we ever be the sold short or the cheated, we who for every service have long ago been overpaid? ~~ Meister Eckhart

      I know that BEGIN is not really a sub but it's kind of a sub ... I wonder why that doesn't work? Is it some sort of weird timing issue?

      Cheers,
      Ovid

      New address of my CGI Course.

        It's not even a kind of sub. It's not anything like a sub at all. You can't manually call the code found in a BEGIN block, can't take a reference to it, or anything else you can do with a sub. The fact that you're allowed to write sub BEGIN is just syntactic sugar — badly misguided syntactic sugar.

        Makeshifts last the longest.

Re: Mini-rant about 'strict'
by perrin (Chancellor) on Aug 04, 2004 at 20:33 UTC
    Since using a variable that you haven't assigned anything to is a core feature of Perl, I don't see how this could be avoided. I think you might have been tripped up by your knowledge of Perl here. A newbie would probably not expect those values to be in %hash before running the line that assigns them to it.

      I think you might have been tripped up by your knowledge of Perl here.

      Thanks, but I think you're being far too generous here :) I was being incredibly stupid and even the warnings pragma didn't help because I was using exists. I spent quite some time debugging keys -- and it took a long time between runs to generate those keys -- before I realized that my keys were perfectly valid the first time I created 'em. It's things like this which make me drool for Haskell at times (where I can validly use the variable prior to the declaration and assignment (well, it's not really declaration and assignment, I suppose.))

      Cheers,
      Ovid

      New address of my CGI Course.

Re: Mini-rant about 'strict'
by tye (Sage) on Aug 04, 2004 at 21:24 UTC

    As several noted, BEGIN helps here. To further discourage such mistakes, I never write a scipt like you wrote. I write something more like:

    use warnings; use strict; Main( @ARGV ); exit( 0 ); my %hash; BEGIN { %hash = ( bar => 'value' ); } sub Foo { print $hash{shift(@_)} } sub Main { Foo('bar'); }

    so that any code outside of subroutines but below the call to Main() never gets run (rather than being run when it might be too late).

    Even more than my %hash = BEGIN { ... };, I'd like:

    sub Foo { my %hash = ( bar => 'value' ) BEGIN; print $hash{shift(@_)}; }

    That is, have BEGIN be available as a block-less statement modifier like 'if', 'for', and 'while'; which gives us "static" variables.

    - tye        

Re: Mini-rant about 'strict'
by hardburn (Abbot) on Aug 04, 2004 at 21:23 UTC

    This is why I structure any sizable program like this:

    # # Load global modules and constant data here # sub foo { . . . } sub bar { . . . } { # Main code block }

    Nothing of real consequence will run until that main code block, and that block keeps variables strictly scoped. On occasion, I need some static data for a specific subroutine, which I create like this:

    { my %hash = ( bar => 1 ); sub foo { . . . } }

    This structure guarentees that the hash will be filled and in scope, without having to resort to BEGIN tricks.

    "There is no shame in being self-taught, only in not trying to learn in the first place." -- Atrus, Myst: The Book of D'ni.

Re: Mini-rant about 'strict'
by tilly (Archbishop) on Aug 04, 2004 at 21:55 UTC
    Just to give a different solution, I'm going to point out that if you moved your functions into another module that you use or require, then you will find that the initializations automatically happen at the right time.

      That is, of course, because you're moving them into an implicit BEGIN block, and thus not a different solution at all.

      Makeshifts last the longest.

        It is not an implicit BEGIN block if you require the module.

        I also think that this solution adds context. It shows why this coding organization won't cause problems most of the time, and gives insight either on when he has to remember to be particularly careful, or else how to avoid it more often. (His choice as to which way to read that.)

Re: Mini-rant about 'strict'
by Aristotle (Chancellor) on Aug 04, 2004 at 22:40 UTC

    Well, it did generate a warning, didn't it?

    Also, I have no idea what strict has to do with any of this.

    I can't see any easy way to fix this either. The obvious thought at this point is that the language might try to guess that variable declarations with an initialization expression are meant to have a value before they are first accessed. But the obvious thought won't work.

    What should happen here?

    my $foo = undef;

    That's gonna be undef on first access. You can construct more complicated scenarios using dynamically created lists where only partial evaluation will suffice to know which variables are supposed to be undef or not beforehand, like

    my ($foo, $bar) = split " ", int rand 42;

    And then this scheme still breaks down because the problem is not limited to compile time.

    sub foo { baz(); my $foo = "bar"; sub baz { print $foo; } }

    Although I'll grant that that's more likely to be a problem in PASCAL than Perl. :-)

    Makeshifts last the longest.

Re: Mini-rant about 'strict'
by ysth (Canon) on Aug 04, 2004 at 21:11 UTC
    Where does strict come into play?

    You can do:

    my %hash; BEGIN { %hash = (bar => 'value'); } ...

      Yes, I know about the BEGIN trick. I could also have just moved the assignment above the sub call. That's not difficult. The annoying bit is that in a large program, it's not always clear that an assignment has not occurred even though the programmer (me, in this case) might be staring at assignment. It can look like a weird scoping issue, hence my desire for strict to catch variables that have never been assigned to as opposed to variables that are merely undefined.

      Cheers,
      Ovid

      New address of my CGI Course.

        If you are going to mix declarations and code outside of subs, you might consider always using the BEGIN trick.