Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses
 
PerlMonks  

Should "use strict" complain if I use a variable that is not declared?

by earthman (Initiate)
on Jun 28, 2010 at 08:06 UTC ( [id://846831]=perlquestion: print w/replies, xml ) Need Help??

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

When designing a class, I have developed the habit of allowing the constructor to take, in the arguments passed to the new() method, extra properties with which to prepopulate the object . This looks something like:

sub new { ... my %self = %{$_[0]} if ref $_[0]; ... }

Recently, I discovered that there is a major problem with the preceding method. The problem is that the condition prevents the "my" if the condition is false and, furthermore, strict does not complain that the "my" failed (or may fail) to occur. If the conditional prevents %self from being declared, using %self results in unexpected behavior that I depended upon "use strict" to prevent.

The following small code example illustrates the problem:

use strict; use warnings; my $a = testref1(); my $b = testref1(); $b->{test}=""; $a->{test}="hello world"; print $b->{test} . "\n\n"; my $c = testref2(); my $d = testref2(); $d->{test}=""; $c->{test}="hello world"; print $d->{test} . "\n\n"; sub testref1{ my %x = %{$_[0]} if ref $_[0]; print "testref1:" . \%x . "\n"; return(\%x); } sub testref2{ my %x; %x = %{$_[0]} if ref $_[0]; print "testref2:" . \%x . "\n"; return(\%x); }

The output on my machine looks like:

testref1:HASH(0x9f952c4) testref1:HASH(0x9f952c4) hello world testref2:HASH(0x9f95864) testref2:HASH(0x9f7b7a4)

What is notable about the output is that the addresses of the hash references returned from testref1 refer to the same hash, while testref2 returns references to two different hashes. This can be further seen by assigning data to the $a hashref and printing this data using the $b hashref. The result, in my production code, is that the new() method of my classes returns the same object over and over, when I would have preferred to have gotten a new() one.

I have no problem separating the variable declarations from any conditions wherever it occurs in my code, but I expected strict to warn me about a failure to declare a variable. When I got no messages from strict, I assumed (incorrectly) that the declaration was exempt from the condition. Should strict complain about a conditional declaration?

Thanks!

Michael Krebs
SpeakBlast.com

Replies are listed 'Best First'.
Re: Should "use strict" complain if I use a variable that is not declared?
by Corion (Patriarch) on Jun 28, 2010 at 08:10 UTC
    my %self = %{$_[0]} if ref $_[0];

    has been deprecated and never really did what you think. See the end of Statement Modifiers in perlsyn about the behaviour or the lack of definedness.

      As previously mentioned, using a variable that was conditionally defined is not valid Perl, but the compiler does not flag it as an error. I shall suggest alternatives.

      The problem can be eliminated from

      sub new { my $class = shift; my %self = %{$_[0]} if ref $_[0]; ... }

      by writing it as

      # Class->new(); # Class->new({ k=>v, ... }); sub new { my $class = shift; my %self = ref $_[0] ? %{$_[0]} : (); ...

      Better yet, allow the curlies to be omitted for free.

      # Class->new(); # Class->new({ k=>v, ... }); # Class->new( k=>v, ... ); sub new { my $class = shift; my %self = ref $_[0] ? %{$_[0]} : @_; ... }

      I always found using the curlies around named args silly. Sounds like a premature optimisation that does little more than make the code noisier.

      There's also the following which doesn't allow hash refs at all:

      # Class->new(); # Class->new( k=>v, ... ); sub new { my ($class, %self) = @_; ... }

      Thank you for this very good pointer to the documentation. Here be dragons, indeed.

Re: Should "use strict" complain if I use a variable that is not declared?
by davido (Cardinal) on Jun 28, 2010 at 08:16 UTC

    From perlsub:

    "A my has both a compile-time and a run-time effect. At compile time, the compiler takes notice of it. The principal usefulness of this is to quiet use strict 'vars' , but it is also essential for generation of closures as detailed in perlref. Actual initialization is delayed until run time, though, so it gets executed at the appropriate time, such as each time through a loop, for example."

    use strict 'vars' is checked at compiletime. Yet at compiletime, the compiler cannot know what the condition will be. It has to assume that under some conditions the variable will be declared, and that being the case, it must squelch the stricture complaint.


    Dave

      I understand that it does squelch the stricture complaint, but must it? I would suggest that it could, as an alternative, assume that under some conditions the variable might not be declared, and complain. It is supposed to be strict, after all.

        The variable is declared. It might just not be initialized. The two are distinct, and strict is only about declaration.

        Corion explained that it's proper for strict not to give an error, but Perl could certainly emit a warning or throw an error.

        The only complication would be backwards compatibility. People rely on the behaviour of a conditionally executed my even though the docs have long warned against it.

        sub foo { my $x if 0; print ++$x, "\n"; } foo() for 1..3;
        1 2 3

        Note that using the hardcoded zero there warns Deprecated use of my() in false conditional.

Re: Should "use strict" complain if I use a variable that is not declared?
by Burak (Chaplain) on Jun 28, 2010 at 20:52 UTC
Re: Should "use strict" complain if I use a variable that is not declared?
by doug (Pilgrim) on Jun 28, 2010 at 18:16 UTC

    I've never liked mixing my and modifiers before, and reading this thread has taught me why. I like to always give explicit initializations, so I end up writing that like

    my %x = (@_ and ref($_[0]) eq 'HASH') ? %{ $_[0] } : ();

    - doug

Re: Should "use strict" complain if I use a variable that is not declared?
by simul (Novice) on Jul 02, 2010 at 04:29 UTC
    The very purpose of "use strict" is to prevent these kinds of declarative errors.

    The person who was talking about "initialized" versus "declared" was factually incorrect. Please look carefully at the code, and the results.

    The variable is NOT being "my'ed" - and the result is that you get a local variable with the same behavior as a *global* (%x is only being "declared" once, and a reference to x returns the same address). Incredible that two "seemingly" locally scoped variables can have the same reference!

    This is broken, plain and simple. my should have precedence. if someone say my $x = 1 if $y ... $x should be 1. declared and either "1" or "undefined". Not "global". That's wonky.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://846831]
Approved by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others avoiding work at the Monastery: (6)
As of 2024-04-23 13:35 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found