Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number
 
PerlMonks  

scope and declaration in local packages

by december (Pilgrim)
on Jan 29, 2011 at 17:41 UTC ( [id://885045]=perlquestion: print w/replies, xml ) Need Help??

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

Hey fellow monks,

I have a question about scope in packages and I hope somebody smarter and with deeper knowledge of Perl internals can explain the resulting error messages to me.

Given the following simplified code:

#!/usr/bin/perl -wT use warnings; use strict; # call object method Animal::Hog->sound(); # here we define the object { package Animal::Hog; my $sound1 = "knor1"; our $sound2 = "knor2"; sub new() { my $this = shift; my $class = ref($this) || $this; my $self = {}; bless $self; return $self; } sub sound() { my $self = shift; print $sound1, "\n"; print $sound2, "\n"; return 'knor'; } }

... I get this result:

Use of uninitialized value $sound1 in print at ./tmp.pl line 30. Use of uninitialized value $Animal::Hog::sound2 in print at ./tmp.pl l +ine 31.

I am wondering why Perl knows about Animal::Hog->sound() without predeclaring it, yet returns an error for the lexical variables $sound1 and $sound2. It confuses me because I expect Perl to either have parsed the section and declared the variables and functions, or for it to not have done so and hence not know about any variables or functions in that block at all.

Duh?

Thanks for any insight!



PS: I know I could move the package declaration above the invocation, use a BEGIN block or declare the objects in another file.

Replies are listed 'Best First'.
Re: scope and declaration in local packages
by chromatic (Archbishop) on Jan 29, 2011 at 18:34 UTC

    Anorny is right. Let me illustrate by walking through your program in two passes. The first stage is the compilation stage. I've elided code that has no effect during compilation.

    #!/usr/bin/perl -T # implicit BEGIN blocks cause require and importing use strict; use warnings; # here we define the object { # creates a namespace package Animal::Hog; # creates, but does not initialize, a lexical my $sound1; # creates, but does not initialize, a strict-friendly global our $sound2; # creates the function sub new { my $class = shift; bless {}, $class; } # creates the function, closing over the lexical $sound1 # ... and binding to the symbol $Animal::Hog::sound2 sub sound { my $self = shift; print $sound1, "\n"; print $sound2, "\n"; return 'knor'; } }

    That's the compilation stage. Runtime looks like this, with irrelevant code elided:

    #!/usr/bin/perl -wT # call object method Animal::Hog->sound(); { # assigns to the variable my $sound1 = "knor1"; # assigns to the variable our $sound2 = "knor2"; }

    Execution happens in statement order.

    (I also cleaned up your code slightly. Prototypes are useless on methods. Single-argument bless is almost always an error. The copy constructor technique using ref is also almost always an error.)

      I've looked around for a way to easily debug scoping problems. I used App::Trace, and Devel::SimpleTrace, which I prefer to diagnostics. I didn't cleanup the code, but I followed chromatic's advice about single-argument bless being almost always an error and fixed the bless; also, I declared $sound1 and $sound2 in your sound sub.
      #!/usr/bin/perl -T use strict; use warnings; use App::Options; use App::Trace; use Devel::SimpleTrace; Animal::Hog->sound(); { package Animal::Hog; sub new { &App::sub_entry if ($App::Trace); my ($this, @args) = @_; my $class = ref($this) || $this; my $self = { @args }; bless $self, $class; &App::sub_exit($self) if ($App::Trace); return($self); } sub sound { my $sound1 = 'knor1'; my $sound2 = 'knor2'; &App::sub_entry if ($App::Trace); my $self = shift; &App::sub_exit($_) if ($App::Trace); print $sound1, "\n", $sound2, "\n"; } if ($App::debug && &App::in_debug_scope) { print "Debug output\n"; } }

      ++ to your explanation. Brings to mind a post of mine from long ago where someone alerted me to the different program execution phases that variable declaration and variable assignment happen in. It was new to me then.

      I am curious about your comment regarding the "copy constructor technique" - I assume you mean a constructor snippet like:

      sub new{ my $proto = shift; my $class = ref($proto) || $proto; bless {}, $class; }
      I am curious because I vaguely recall some prior nodes deriding this practice, mostly as I recall claiming that it's cargo-cult coding and the person writing the code doesn't understand what it does. But what makes you say that this usage is almost always an error? As long as you understand what it does, it seems like an acceptable shortcut to object instantiation, although I personally wouldn't use it. Is there some hidden danger here that I don't know about? If I remember correctly, my (outdated) edition of The Camel uses code like this.

        But what makes you say that this usage is almost always an error?

        In the original code, $class went entirely unused. All of the shenanigans to figure out whether the invocant is a reference are useless.

        The design problem with this technique is that it allows you to write:

        my $object = Class->new(); my $otherobj = $object->new();

        What relationship should $otherobj have with $object? Does it have any? It's easy to expect a prototypal relationship between the two, or one where the former's instance data somehow sets the latter's, but there's almost never any code in the copy-and-paste copy constructor to set this.

        Why create an interface that does nothing?

        Worst yet, consider what happens if someone invokes the constructor like this:

        o my $object = Class::new( {} );

        I know that's unlikely, but the copy constructor technique will silently create an object blessed into a package called HASH. Without the copy constructor technique, Perl will happily throw an exception about attempting to bless into a reference. This technique silences useful error messages for no good reason.

        As far as I can tell, the only reason this code persists is because someone put it in the documentation as an example of a cool technique, and people started thinking it was necessary because the documentation explained Perl 5 objects as crazy black boxes that require a lot of arcana.

      Thanks. I didn't realise that the function vs. variable "declaration" happens in different stages.

      I used the "ref instance constructor" this time because I've seen it around and it seemed like a nice extra feature to have. I doubt if it's really useful to my code, though... Probably it's cleaner without.

        I didn't realise that the function vs. variable "declaration" happens in different stages.

        Function and variable declarations both happen at compile time. You wouldn't have been able to compile the function if that wasn't the case. How can it use a variable that doesn't exist?

        Assignments, on the other hand, happen at run-time. That's why the following code works:

        >perl -E"for (qw( a b )) { my $x = $_; say $x }" a b
Re: scope and declaration in local packages
by ikegami (Patriarch) on Jan 29, 2011 at 18:06 UTC

    It's not a scope problem. The problem is that «Animal::Hog->sound();» comes before «my $sound1 = "knor1";» and «our $sound2 = "knor2";». You're basically inlining a module, but you're not properly emulating «use». To do so, use

    BEGIN { package Animal::Hog; ... $INC{'Animal/Hog.pm'} = __FILE__; }

    In this case, you can avoid doing the last statement. Simply adding «BEGIN» would do the trick.

      I realised that I could use a BEGIN block, but I was basically wondering why Perl didn't complain about the functions, yet did so with the variables.

        So you think the function exists and the variable doesn't exist? That's not the case. The function and the variables are all declared at compile time. It's not complaining about the variables not existing. It's complaining that you didn't put a value in them, and that's because you didn't execute the assignment.

        my $sound1 = "knor1";

        is the same as

        my $sound1; $sound1 = "knor1";

        Many language have a special declaration syntax that allows for the initialisation of variables, but Perl does not. my always initialise the variable it creates to undef* (scalars) or empty (arrays and hashes). If you want it to have another value, you need to assign a value to it.

        * — Implementation differs slightly, but you're not suppose to ever encounter that.

Re: scope and declaration in local packages
by Anonyrnous Monk (Hermit) on Jan 29, 2011 at 18:06 UTC

    You're calling Animal::Hog->sound() too early for the variables to have been assigned a value.  Put the call after the block wherein you define the package, and things will work. Or use a BEGIN block.

    The initialisation of the variables happens at runtime (execution of the block), while the setting up of the subroutines happens at compile-time.

    (Also, in a real program, you'd probably want to call the constructor (new) to create an object instance — otherwise you might as well use regular non-OO subroutines (as opposed to methods)...)

      The initialisation of the variables happens at runtime (execution of the block), while the setting up of the subroutines happens at compile-time.

      That explains my confusion as to why Perl doesn't complain about the functions but can't find the variables. Thanks!

      The reason I'm not calling new is that I would like to encapsulate a regex (and relevant condition) inside the object which should work without actually creating an instance. I just want to store that regex in the object for cleanliness, apart from any actual OO code.

Re: scope and declaration in local packages
by JavaFan (Canon) on Jan 29, 2011 at 18:11 UTC
    On top of what's said above, you're calling sound as a class method, however, the code of the sub suggests it ought to be called as an object method. However, your snippet never creates an object.

      The code is a lot larger than that. This function however just checks a regex and should work without any instance being in existence, i.e. as a class method. I just wanted to store the regex inside the object so I can check if any input can actually be made into this object without actually trying to create an instance. This one function isn't actually strictly speaking OO at all.

      In pseudo-code, I just want to be able to say: if Dog::looks_like_a_dog, then dog = Dog->make_a_dog().

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others admiring the Monastery: (3)
As of 2024-04-19 02:27 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found