http://qs321.pair.com?node_id=1183752

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

In general having "use warnings" on is a good idea, but at least in one case it makes code unnecessary complex. Specifically, when I would like to print out debugging messages. For examples this code:
use 5.010; use strict; use warnings; my $x = 23; my $y; debug("x='$x' y='$y'"); debug(sprintf "x='%s' y='%s'", ($x // ''), ($y // '')); sub debug { say shift }
the first call to debug() will generate a warning "Use of uninitialized value $y in concatenation..." The second call to debug() does not generate any warning, but it seems to be too complex. Another solution I was thinking about is probably even more complex:
use 5.010; use strict; use warnings; my $x = 23; my $y; debug({x => $x, y => $y}); sub debug { my ($d) = @_; my $s = join ' ', map { sprintf("$_='%s'", ($d->{$_} // '')) } sor +t keys %$d; say $s; }
Another solution might be this:
use 5.010; use strict; use warnings; my $x = 23; my $y; debug("x='%s' y='%s'", $x, $y); sub debug { my ($fmt, @params) = @_; printf "$fmt\n", map { $_ // '' } @params; }
Do you see any problems with the suggested solutions? Do you have any other suggestions?

Replies are listed 'Best First'.
Re: use warnings and debug messages
by Eily (Monsignor) on Mar 06, 2017 at 12:43 UTC

    Maybe disabling the warning locally is the less cumbersome solution:

    { no warnings "uninitialized"; debug("x='$x', y= '$y'"); }
    (Or with the no warnings inside the debug function if you use the hash version).

    But actually, if the purpose is debug, being able to tell the difference between undef and the empty string is probably useful. Maybe with Data::Dump:

    use v5.10; use warnings; use Data::Dump qw( pp ); sub debug { if (@_ > 1) { say pp { @_ }; } else { say pp $_[0]; } } my $x = 23; my $y; debug([$x, $y]); debug(x => $x, y => $y); debug("Hello"); debug("Hello\n"); __DATA__ [23, undef] { x => 23, y => undef } "Hello" "Hello\n"
    It also displays \r \n that might go otherwise unnoticed (eg: you have the two when you only expected \n)

    Edit: corrected link to Data::Dump

Re: use warnings and debug messages
by Athanasius (Archbishop) on Mar 06, 2017 at 13:30 UTC

    Hello szabgab,

    I actually like your own proposed solutions. But here is another possibility:

    use 5.010; use strict; use warnings; use constant DEBUG => 1; no if DEBUG, warnings => 'uninitialized'; my $x = 23; my $y; say "With DEBUG = ", DEBUG; debug("x='$x' y='$y'") if DEBUG; if (DEBUG) { sub debug { say shift; } }

    Output:

    22:41 >perl 1758_SoPW.pl With DEBUG = 0 22:41 >perl 1758_SoPW.pl With DEBUG = 1 x='23' y='' 22:41 >

    The downside, of course, is that you have to add if DEBUG to the end of all your existing debug calls. :-(

    Anyway, hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

Re: use warnings and debug messages
by salva (Canon) on Mar 06, 2017 at 14:50 UTC
    I have used something similar to the following function in the past:
    sub debug { warn join '', map $_ // '<undef>', @_, "\n"; } # and then... debug "x=", $x, " y=", $y;

    Update: and sometimes also a -f variant:

    sub debugf { my $fmt = shift; warn sprintf($fmt, map $_ // '<undef>', @_) . "\n"; } debugf "x=%s y=%s", $x, $y; # <-- always using %s!
Re: use warnings and debug messages
by LanX (Saint) on Mar 06, 2017 at 15:18 UTC
    Hi Gabor

    > Do you see any problems with the suggested solutions?

    Yes, if a value is undef I want to see undef in the debug output, not an empty string.

    And I want to be able to distinguish the string "undef" from undef.

    Consequently you should use Data::Dump which already handles those cases and more

    #extended usage with names print Data::Dumper->Dump([$foo, $bar], [qw(foo *ary)]);

    of course that's cumbersome, so you want to put it in a custom debug routine, like your hash syntax

    But of course you might not really want to repeat the var name.

    Then you can use an approach like

    Data::Dumper::Lazy

    use Data::Dumper::Lazy; @a = 1..5; dmp {@a};

    Contrary to other solutions relying on source filter or Padwalker this will also work within string eval.

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Je suis Charlie!

Re: use warnings and debug messages
by 1nickt (Canon) on Mar 06, 2017 at 12:48 UTC

    Odd objective IMO. I like being warned when something I expect to have a value doesn't. But you can always turn off warnings in your debug sub if you don't like to see them:

    use 5.010; use strict; use warnings; my $x = 42; my $y; my $z; debug($x, $y); say $z; sub debug { no warnings 'uninitialized'; say join ',', @_; }
    Output:
    42, Use of uninitialized value $z in say at szab.pl line 11.

    Hope this helps!


    The way forward always starts with a minimal test.
      We have tons of these debug() calls and in most cases we don't care if the value is undef or the empty string.

      We currently don't have "use warnings" in the code and turning them on will generate a lot of warnings that are useless to us making it hard to locate the important ones.

        Isn't "turning them on" a fairly cheap price of doing business?

        Clearly, your problem case suggests that an option might be to "use warnings" after you've dealt with the more significant warnings errors/problems that are apt to turn up in new code... and even then, the price of going thru "tons" of code to check the then-new onslaught of warnings seems to me to be insignificant compared to the price of an error that gives away your product or that causes a client to suffer harm.

        And one more thought:

        • ...if the "uninitialized" warning is your real concern, it might suggest that some re-thinking the timing and manner of instantiating variables would be appropriate... to clean up what might be considered a code-smell.

        If that seems too expensive, or the types of warnings are more diverse than you feel you can afford to deal with, perhaps its time to consider that the cost of selectively inserting "no warnings uninitialized" suggested by Vicar Eily is likely to be NO HIGHER and to pay the bonus of being clearer.

        Edit: Strike/corrected language in para 1; fixed markup on "code smell" and fixed "that" to "than" in final para.


        Spirit of the Monastery

        ... but, check Ln42!

        So is your objective to be able to enable use warnings;?

        Perhaps write a sig handler for warn, and evaporate (or do something else with) all uninit warnings?:

        use 5.010; use strict; use warnings; $SIG{__WARN__} = sub { my $w = shift; if ($w !~ /uninitialized/){ warn $w; } }; my $x = 23; my $y; debug("x='$x' y='$y'"); sub debug { say shift; }
        We currently don't have "use warnings" in the code and turning them on will generate a lot of warnings that are useless to us

        You know, these warnings are not there to make your life harder. It's not that perl takes your code as seed for a PRNG and then generates warnings from that PRNG. Every single warning generated has a reason, and most time, it is bad code or a possible error.

        making it hard to locate the important ones.

        If your code is such a mess, start by temporarily disabling the most frequent warnings - see warnings, or remove them by filtering STDERR, if you can't easily fix them.

        Alexander

        --
        Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
        It should be possible to write your own Our::warnings pragma in order to activate only the finegrained warnings you want.

        that's certainly better than using no warnings at all.

        Cheers Rolf
        (addicted to the Perl Programming Language and ☆☆☆☆ :)
        Je suis Charlie!

        update

        Something like

        sub import { my ($class, $date) = @_; ... warnings->import(@wanted_warnings); ... }
Re: use warnings and debug messages
by GrandFather (Saint) on Mar 07, 2017 at 20:26 UTC

    If the nature of the debug statements really is of the nature of debug("x='%s' y='%s'", $x, $y); then throw away your debug statements and invest in a decent IDE instead. With an IDE you don't need to decide up front what you need to dump - just set a break point and inspect anything that looks interesting. Then you can follow an interesting train of execution in one hit or even alter the variable's value to check following code does the right stuff. Much better than stopping execution, adding and removing debug statements and rerunning the code followed by an intense session of wading through pages of output using Sherlock level detective skills to try and determine what went on.

    I almost never use debugging prints. I sometimes add a chunk of conditional code that I can set a break point on. I always have strictures turned on, even for "one line" trivial code.

    Mind you, the example you use causes flashing red lights and alarm bells to go off when I read it. Declaring $y without initializing it is almost always an error. Rather than sweep those errors under the carpet by turning off warnings it would be better to get an intern to find all those cases and either make obvious fixes or flag them for intervention by a higher authority. Some of those intervention cases are going to make the whole exercise worth while!

    Premature optimization is the root of all job security