Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation

Duh. 'my' scope in if else blocks.

by gam3 (Curate)
on Dec 10, 2007 at 22:11 UTC ( #656250=perlquestion: print w/ replies, xml ) Need Help??
gam3 has asked for the wisdom of the Perl Monks concerning the following question:

I was running some code today with warnings on (using 5.8.8) and I noticed something new.

I did not know that the following code was correct:

use strict; use warnings; sub bob { if (!(my $t = shift)) { warn "data is false\n"; } elsif ($t eq 'a') { warn "a\n"; } elsif ($t eq '1') { warn "one\n"; } else { warn $t; } } bob(); bob(1); bob('a'); bob('c');
I had thought that the scope of $t was only the if block and did not extend to the else blocks. Of course it makes perfect sense now. I just thought I would mention it here on the off chance that there is some other perlmonk as ignorant as me.

BTW I noticed this because I was running with use warnings, something I don't normally do.

UPDATE: [Thilosophy]
I got a warning because I had:

if (my $a = $c) { #... } elsif (my $a = $b) { #... }
This gives the warning:
"my" variable $a masks earlier declaration in same scope at ... 
-- gam3
A picture is worth a thousand words, but takes 200K.

Comment on Duh. 'my' scope in if else blocks.
Select or Download Code
Replies are listed 'Best First'.
Re: Duh. 'my' scope in if else blocks.
by bobf (Monsignor) on Dec 10, 2007 at 23:17 UTC

    This is documented in perlsub:

    Similarly, in the conditional
    if ((my $answer = <STDIN>) =~ /^yes$/i) { user_agrees(); } elsif ($answer =~ /^no$/i) { user_disagrees(); } else { chomp $answer; die "'$answer' is neither 'yes' nor 'no'"; }
    the scope of $answer extends from its declaration through the rest of that conditional, including any elsif and else clauses, but not beyond it.

Re: Duh. 'my' scope in if else blocks.
by doom (Deacon) on Dec 11, 2007 at 01:50 UTC

    It makes sense to me because the "my" is up outside of the curlies. This wouldn't work, for example:

    if ( scalar( @_ ) ) { my $t = shift; $t =~ s/change_something/to_something_else/; } print "$t\n";
    Parenthencies don't influence lexical scope.

      This won't work either, even though my $t is outside of the curlies (but inside the parens) now:
      if ( my $t = shift ) { $t =~ s/change_something/to_something_else/; } print "$t\n"; # out of scope here
      if behaves similar to for in this respect.

      Somewhat surprisingly then, this does not work:

      print $t if (my $t = shift);

        doom's explanation is wrong. It has nothing to do with order. Even if you fixed the order, it still wouldn't work.

        (my $t = shift) and print $t; # Fails.

        Variables cannot be used (by name) in the statement in which they are declared. This allows statements such as my $t = $t; to work.

        print $t if (my $t = shift); # Fails. my $t = shift; print $t if $t; # Ok. if (my $t = shift) { print $t; } # Ok.

        if behaves similar to for in this respect.

        Yes good point. I always forget about that, too... sometimes you'd like to have the loop counter defined after the loop is over, but you have to make a special effort to do that. This won't work, for example:

        my @list = qw( wuhn tew thuree foah DONE whateva you know); for my $i (0..10) { last if ($list[$i] eq 'DONE'); } print "final index: $i\n";

        Somewhat surprisingly then, this does not work:
        print $t if (my $t = shift);

        Well, I wouldn't say that that's exactly a surprise -- after all, it's a fairly odd thing to do, declaring it's a lexical after you use it... This doesn't work either:

        $t = shift; print $t; my $t;

Re: Duh. 'my' scope in if else blocks.
by Thilosophy (Curate) on Dec 11, 2007 at 02:53 UTC
    I noticed this because I was running with use warnings, something I don't normally do.

    What warning did you get? I am on 5.8.8, too, and there is no warning.

    Or did you notice the absence of a warning you expected? But if you expected $t to be out of scope, that should have been a compile error (because of use strict) rather than a warning.

Re: Duh. 'my' scope in if else blocks.
by ikegami (Pope) on Dec 11, 2007 at 19:20 UTC
    In addition to all the body blocks, the whole if statement is a block of its own. Just like for and foreach loops.
      In addition to all the body blocks, the whole if statement is a block of its own. Just like for and foreach loops.
      Note though that we very intentionally broke this in Perl 6. It's one of those places where you have to keep track of a list of exceptions in Perl 5, and all such lists are a design smell. (The right way to fix it was to fix the for/foreach syntax, not the if syntax. A loop variable should be lexical to its block by virtue of being a formal parameter to that block, not because it just happens to be mentioned in an exceptional syntax.)

        Heh, I've long disliked the "invisible" block around Perl if() constructs. It belies the term "lexical scope" to me. I resolved this dislike by mostly refusing to declare lexicals inside of conditional expressions.

        This reminds me of my long-standing frustration with the adamant, unconditional localization of loop variables. Default localization of loop variables seems a wise choice but there have been far too many times where part of my purpose in looping over some values was to pick one of them. And you can't do that "the obvious way" in Perl. You get the relatively awkward:

        my $selected; for my $candidate ( ... ) { $selected= $candidate; last if ...; }

        localization of loop variables preceeded 'my' so, given the time-machine-like oportunity of Perl 6 breaking backward compatability in a backward-compatible way, I'm tempted to propose that "for my $foo (" continue to effectively localize (by creating a new lexical $foo) but change "my $foo; ...; for $foo (" to leave the last looped value in $foo. But, of course, that gets the Huffman coding backward and is convoluted enough to be unwise.

        I re-read the section of a4 about 'foreach' and didn't see this mentioned. So I'm assuming localization is still unconditional. It makes me long for an alternative to avoid the localization. But having "for @choices -> my $chosen {" serve that purpose doesn't scratch the itch as well as I'd like and also leaves too many scratch marks.

        But maybe you've already designed a better way to do this (and probably described it and I've probably read the description and either forgotten it or not understood the implications of it :). Perhaps "reusing" a variable name from the current lexical scope as a formal parameter should mean that this variable is actually used as the one that the parameter is bound to and the (last) bound parameter value is left in that variable. In the short time I've considered that idea, and with so little of the rest of Perl 6 swapped in, I think I might like that idea.

        - tye        

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://656250]
Approved by Fletch
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others browsing the Monastery: (2)
As of 2015-11-28 11:38 GMT
Find Nodes?
    Voting Booth?

    What would be the most significant thing to happen if a rope (or wire) tied the Earth and the Moon together?

    Results (741 votes), past polls