http://qs1969.pair.com?node_id=11135932

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

I want to use the same name for a var above and inside braces, but in some cases, I want to make a local copy to use in the braces:
# sorta like my $self = new OO( $cat ); { my $self = new OO( $dog ) if $ARGV[0] eq 'Fido'; say $self->species(); }
.. but at the "say" line, self is undef when ARGV[0] ne 'Fido'; Which surprised me, since I thought OK- it wont create a new scope of $self in the braces, so the scope above should persist? I also tried ( my $self = new OO( $dog ) ) if $ARGV[0] eq 'Fido'; which I thought had an even better chance to succeed but didn't.

Seems counter-intuitive that ANY part inside of ( ) would have any influence, if followed by "if 0"?

When I emerge from the braces I want the original self there, and I dont want to use another variable name since I have hundreds of "self" refs in the braces... I also tried "local" instead of "my" but got a lexical error in that case.

TY

Replies are listed 'Best First'.
Re: Not sure how to handle this scope (updated)
by haukex (Archbishop) on Aug 18, 2021 at 18:04 UTC

    I wouldn't re-use the same variable name for a lexical that has already been defined. Also, don't put a postfix if on a my, as its behavior is undefined - see the note at the bottom of the section Statement Modifiers. Instead, you could do:

    my $self = OO->new( $cat ); { my $inner_self = $ARGV[0] eq 'Fido' ? OO->new( $dog ) : $self; say $inner_self->species(); }

    Note you shouldn't use Indirect Object Syntax either.

    Update: Added some details to the above. Plus, why don't you just do this?

    my $self = OO->new( $ARGV[0] eq 'Fido' ? $dog : $cat );

    It's also a bit unusual to use $self as the variable name for a newly created object, since $self is usually used as the first argument to a method call, i.e. it's an existing object.

      Strongly agree. "Re-using" a variable name, particularly one like $self, is a recipe for obtuse code that will be very difficult for your inevitable successor to understand and debug.
        Thanks guys, I left out a lot of details- but the { } are actually a forked loop and the self that contains the mysql handle goes away for each fork. That's why I'm trying to do this- BUT I want to run sometimes forked , and sometimes not, and for the not case- I want to use the object from outside the loop.. I realize this if pretty fugly but there is a lot of motivation to do it.

        But aisde from all that the real question is why does self go undef, and not use the scope outside the braces?

Re: Not sure how to handle this scope
by tybalt89 (Monsignor) on Aug 18, 2021 at 19:12 UTC

    Something like this? (simplified...)

    #!/usr/bin/perl use strict; use warnings; my $self = 'cat'; print "outside $self\n"; { my $self = $self; print " inside $self\n"; $self = 'dog' if not defined $ARGV[0]; print " inside $self\n"; } print "outside $self\n";

    Outputs:

    outside cat inside cat inside dog outside cat

      It’s silly and off topic but I am compelled to quote it for the tangent.

      Outside of a dog, a book is a man’s best friend. Inside of a dog, it’s too dark to read.
Re: Not sure how to handle this scope
by BillKSmith (Monsignor) on Aug 19, 2021 at 01:34 UTC
    In this respect, lexical variables are much like local. The new variable is not initialized.
    use strict; use warnings; use feature 'say'; package OO; sub new {my $animal = $_[1]; return bless \$animal;} sub species { return ${$_[0]} } package main; my $cat = 'cat'; my $dog = 'dog'; my $self = new OO( $cat ); { my $self = $self; $self = new OO( $dog ) if $ARGV[0] eq 'Fido'; say $self->species(); } say $self->species();

    OUTPUT (with argument 'Fido')

    dog cat

    OUTPUT (without argument 'Fido')

    cat cat
    Bill
Re: Not sure how to handle this scope
by Anonymous Monk on Aug 19, 2021 at 13:52 UTC

    Braces always create a new scope, and my always creates a lexical variable, which is undef by default -- or at least, should be. In practice the behavior of my $x = 'foo' if $bar is undefined if the condition is false, and is documented as such.

    Since the scope of a lexical variable begins at the statement after the statement that defines it, the ternary operator does what you want: my $self = $ARGV[0] eq 'Fido' ? OO->new( $dog ) : $self;. The $self in the assignment statement refers to the variable outside the braces, since the variable inside the braces is not yet in-scope.

      "Braces always create a new scope"

      Unless they create a $hash ={a=>1} or are used for enclosing ${identifiers} or are used to dereference a $hash->{a} ... or...?

      - Ron

        Quite right. In trying to address the OP's statement that ... it wont create a new scope of $self in the braces ... I overstated the case. Thank you for the correction.