http://qs321.pair.com?node_id=885055


in reply to scope and declaration in local packages

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.)

Replies are listed 'Best First'.
Re^2: scope and declaration in local packages
by Khen1950fx (Canon) on Jan 30, 2011 at 00:01 UTC
    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"; } }
OT: copy constructor
by crashtest (Curate) on Jan 30, 2011 at 05:14 UTC

    ++ 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.

Re^2: scope and declaration in local packages
by december (Pilgrim) on Jan 31, 2011 at 13:24 UTC

    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