in reply to Re: A cleaner way of scoping variables
in thread A cleaner way of scoping variables

This code has a problem: if the condition is false, the condition is the last expression evaluated in the do block, so whatever the condition evaluates to becomes the result of the do block.

$ perl -le'print do { "x" if 1 }' x $ perl -le'print do { "x" if 0 }' 0

In your case, the value of $foo is assigned to $bar if $foo is false. This could be particularly nasty because you'll never notice if you don't run tests with $foo being 0 or the empty string rather than undefined.

You must make sure that your do blocks always evaluate to an intended result. In this case, you have to add an else { undef } clause.

Makeshifts last the longest.

Replies are listed 'Best First'.
Re^3: A cleaner way of scoping variables
by ambrus (Abbot) on Aug 09, 2004 at 13:59 UTC
    You must make sure that your do blocks always evaluate to an intended result. In this case, you have to add an else { undef } clause.

    Indeed.

    In your case, the value of $foo is assigned to $bar if $foo is false.

    Perl is a bit wierd about the return value of a loop of which the body never runs. This code:

    #!/usr/bin/perl -w my $true = "foo"; my $false = "0"; my $dc = "bar"; warn do { $dc if $false }; warn do { $dc unless $true }; warn do { $dc while $false }; warn do { $dc until $true }; warn do { if ($false) { $dc } }; warn do { unless ($true) { $dc } }; warn do { while ($false) { $dc } }; warn do { until ($true) { $dc } }; warn do { $dc if "0" }; warn do { $dc unless "foo" }; warn do { $dc while "0" }; warn do { $dc until "foo" }; warn do { if ("0") { $dc } }; warn do { unless ("foo") { $dc } }; warn do { while ("0") { $dc } }; warn do { until ("foo") { $dc } }; __END__
    prints:
    Useless use of private variable in void context at a line 9. Useless use of private variable in void context at a line 10. Useless use of private variable in void context at a line 13. Useless use of private variable in void context at a line 14. Useless use of private variable in void context at a line 21. Useless use of private variable in void context at a line 22. 0 at a line 7. foo at a line 8. 0 at a line 9. foo at a line 10. 0 at a line 11. foo at a line 12. 0 at a line 13. foo at a line 14. 0 at a line 15. foo at a line 16. 0 at a line 17. Warning: something's wrong at a line 18. 0 at a line 19. Warning: something's wrong at a line 20. Warning: something's wrong at a line 21. Warning: something's wrong at a line 22.

      As often, Deparse comes to the rescue. With formatting adjusted for comparability with yours, the output is

      #!/usr/bin/perl -w my $true = "foo"; my $false = "0"; my $dc = "bar"; warn do { $dc if $false }; warn do { $dc unless $true }; warn do { $dc while $false }; warn do { $dc until $true }; warn do { if ($false) { $dc; } }; warn do { unless ($true) { $dc; } }; warn do { while ($false) { $dc; } }; warn do { until ($true) { $dc; } }; warn do { '0' }; warn do { 'foo' }; warn do { do { '0' } }; warn do { do { !1 } }; warn do { '0' }; warn do { !1 }; warn do { }; warn do { };

      Note the !1 — ie "not true", ie "The Real False", which is an empty string in Perl5. That explains your something's wrong messages: the loops are getting folded away at compile time, but apparently the compiler doesn't evaluate hard enough.

      Makeshifts last the longest.

        Nit: "The Real False" is not the empty string. It's the empty string in string context and 0 in numerical context. You can see the differnce using the following snippet:

        use warnings; print(("")+1, "\n"); print((!1)+1, "\n");

        If "The Real False" was simply an empty string, we'd see two warnings instead of

        Argument "" isn't numeric in addition (+) at script.pl line 2. 1 1