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

Is there a way to get perl to tell you what exacly is uninitialized when the warning comes?

Eg.
print "$a$b$c$d$e$f$g$h$i{h}";
Produces _one_ "Use of uninitialized value" warning, but which one is it?

T I M T O W T D I

Replies are listed 'Best First'.
(jeffa) Re: uninitialized value
by jeffa (Bishop) on Jan 22, 2002 at 23:31 UTC
    Perl can only tell you which line, so break it up:
    print $a; print $b; print $c; print $d; print $e; print $f; print $g; print $h; print $i{h};

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    
      Which wont work always :(
      try
      print eval('qq{'.$str1.'}'}; print eval('qq{'.$str2.'}'}; print eval('qq{'.$str3.'}'};
      This gives: "Use of uninitialized value in concatenation (.) at (eval 28) line 1."
      First which line is it talking about? And what value?

      T I M T O W T D I
        The solution that merlyn pointed me at is to use a #line directive like described at the very end of perlstyle. (Bug alert, in Perl 5.005 you need to have a blank line before the directive.)

        You can even wrap this up in a function with error handling like this:

        use Carp; sub my_eval { my ($pkg, $file, $line) = caller(); my $eval_str = qq(\n# line 1 "eval for file '$file' line $line"\n) . shift; if (wantarray()) { my @res = eval($eval_str); return $@ ? confess($@) : @res; } else { my $res = eval($eval_str); return $@ ? confess($@) : $res; } }
        But do be warned, your eval takes place in the scope of my_eval, and not the scope of the function you call it from. (ie You cannot access lexicals.) Unless you somewhere explicitly preprocess your code before compiling it you can't solve this generically without inlining repeated logic.

        Doing this in Perl generally requires some sophisticated hackery of the kind that gets you talked about. For the record this is exactly the kind of problem that Lisp macros are perfect for, factoring out repetitive logic in a generic way while keeping current scoping.

        Well, it works for the orginal example you gave. ;)

        (eval 28), as far as i know, means the 28th eval that has been processed. As for what value, well, you will just have to inspect that yourself. I recommend printing the the value to see what it is, just remove the eval and run it. Then, you should have a clue as to which variable is undefined.

        jeffa

        L-LL-L--L-LL-L--L-LL-L--
        -R--R-RR-R--R-RR-R--R-RR
        B--B--B--B--B--B--B--B--
        H---H---H---H---H---H---
        (the triplet paradiddle with high-hat)
        
Re: uninitialized value
by chromatic (Archbishop) on Jan 22, 2002 at 23:52 UTC
    There are two debugging approaches. The true way is to use a print statement, perhaps print "($a)<$b>[$c]{$d}!$e!|$f|#$g#~$h~>$i{h}<\n"; (though that makes me wonder if you meant $h).

    The other approach is also pretty good, and it's to use the debugger, set a breakpoint on that line, and to use x $a to print the value of the variable in question at that point.

    It might be worth exploring how to make the warning a little more explicit, but since there are a lot of things that can produce an undefined value, it may be quite complex.

Re: uninitialized value
by count0 (Friar) on Jan 22, 2002 at 23:49 UTC
    As a preventative measure, this is a good rule of thumb to follow:
    Whenever you aren't sure if a variable has been assigned a value, give it one explicitly =)

    The || operator is very useful here.
    Examples:
    my $variable = $some_unknown_input || 'default'; my $foo = (defined $input ? $input : '');
    In the first example, if $some_unknown_input is not a true value, $variable will be assigned 'default'.
    In the second example, if $input is undefined (note this is not necessarily the same as being non-true, as in the first example), it is assigned an empty string.