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

Hello, once more, to all you most esteemed residents of this most wonderful inter-web resource,

Once more I prostrate myself before you in seeking the answer to a problem that's had me tearing my hair out - I had little enough to start with;-)

The following is, I believe, valid code - but when compiled, generates warnings (c/w a not entirely unexpected error - that I can explain) and for the life of me, I can't see the problem.

The code (from file SVC/DnsEntity.pm):

package SVC::DnsEntity::IPv6; use strict; use warnings; use Carp; my $value; sub new { my $self = shift; bless \( my $scalar ), ref $self || $self; } sub value { $value } package SVC::DnsEntity::IPv4; use strict; use warnings; use Carp; my $value; sub new { my $self = shift; my $_value = shift || croak "No IP address?!"; croak "Invalid IP address" unless $_value =~ /(:?\d{}1,3){4}/; map { croak "Octet out of bounds" unless $_ >= 0 && $_ <= 255 } split /\./, $_value; bless \$_value, ref $self || $self; } sub value { my $self = shift; $$self; } package SVC::DnsEntity; use strict; use warnings; use Carp; use Net::Ping; use Fatal qw/Net::Ping::new Net::Ping::ping/; use SVC::CmdSession; use vars q/%attribs/; BEGIN { no strict q/refs/; map { my $_sub = $_; eval { *$_sub = sub { $attribs{$_sub} } }; croak "Failed to create $_sub - $@" if $@; } qw/hostname ip_address/; } %attribs = ( hostname => undef, type => q/IPv4/, ip_address => undef, pinger => undef, ); my $DEFAULT_TIMEOUT = 1; sub new { my $self = shift; my $args = { ( %attribs ), @_ }; croak "No IP version type - expected IPv[46]" unless $args->{type}->can(qw/new/); my $ver_class = __PACKAGE__ . "\:\:$args->{type}"; warn "ver_class: $ver_class"; $args->{ip_address} = $ver_class->new($args->{ip_address}) if $args->{ip_address}; $attribs{pinger} = Net::Ping->new(); bless \( my %data = %attribs ), ref $self || $self; }
The warnings & errors:
"my" variable $value masks earlier declaration in same scope at SVC/Dn +sEntity.pm line 28. Subroutine new redefined at SVC/DnsEntity.pm line 76. Can't bless non-reference value at SVC/CmdSession.pm line 17.
It's almost like the compiler doesn't see &/or recognise the package statements ...

I would be obliged if someone could point out what I am patently too close to see.

TIA & Rgds,

Update

Many thanx to zentara and Fletch who, between them pointed out just how much I couldn't see the wood for the trees.

A user level that continues to overstate my experience :-))

Replies are listed 'Best First'.
Re: To be, or not to be, a package? That is the question
by Fletch (Bishop) on Dec 04, 2008 at 13:59 UTC

    The problem is that package doesn't in and of itself introduce a new lexical scope so the previous $value (declared in the outer file-wide scope) is still valid. If you're trying to keep things grouped in the same file then wrap each package's contents in curlies so that there's a separate explicit lexical scope (or just put things in multiple files to begin with . . . :).

    Update: Added parenthetical on file scope.

    The cake is a lie.
    The cake is a lie.
    The cake is a lie.

      I take your point Fletch, but doesn't package introduce a new namespace - so perl should be able to distinguish between the instances of the new() subs ... shouldn't it ? ... or have I missed that boat as well ?

      A user level that continues to overstate my experience :-))

        Yes it introduces a new namespace, but again lexical variables have nothing to do with the symbol table and package variables. Maybe this and a reading of Coping with Scoping will help.

        use strict; use warnings; ## Define a lexical variable $foo (which will be otherwise unrechable +after ## the next our save by the closure) my $foo = 'file lexical'; sub closure { print "closure says: $foo\n" } print "before our: $foo\n"; ## This masks the above lexical $foo for the rest of the outer file sc +ope our $foo = '$main::foo'; package One; ## And this masks the prior lexically visible $foo that pointed to $ma +in::foo our $foo = '$One::foo'; { package Two; ## This $foo temporarily masks the prior $One::foo our $foo = '$Two::foo'; print "inside package Two: $foo\n" } ## Prior our $foo was scoped to the explicit block, so unadorned $foo ## once again means $One::foo . . . print "back outside Two's block: $foo\n"; package main; print "unadorned \$foo: $foo\n"; ## But we can still explicitly refer to each of them by full package n +ame { no strict 'refs'; print "$_: ${$_}\n" for qw( main::foo One::foo Two::foo ); } ## And now the sub closure is the only thing which has a handle on the ## original lexical $foo closure(); exit 0; __END__ before our: file lexical inside package Two: $Two::foo back outside Two's block: $One::foo unadorned $foo: $One::foo main::foo: $main::foo One::foo: $One::foo Two::foo: $Two::foo closure says: file lexical

        Update: Expanded example with more chattiness and a closure.

        The cake is a lie.
        The cake is a lie.
        The cake is a lie.

        As Fletch said, a namespace is not the same as a lexical scope...

        This is important to keep in mind if you merge several modules (.pm files) into one file — for easy distribution, or whatever. Forgetting to put extra braces around each original file / package can introduce nasty subtle bugs... (in particular if the modules are coded sloppily, with lots of global vars, no strictures, etc.).

        The behavior is mentioned in the package docs, btw:

        (...) A package statement affects only dynamic variables--including those you've used local on--but not lexical variables, which are created with my.
Re: To be, or not to be, a package? That is the question
by merlyn (Sage) on Dec 04, 2008 at 14:08 UTC
      As I haven't got my copy of PBP in front of me, pls advise as to the preferred way.

      TIA ,

      A user level that continues to overstate my experience :-))
        I don't know what PBP has to say on the matter, as its one of the few Perl books I haven't bought. But...

        sub new { my $self = shift; bless \( my $scalar ), ref $self || $self; }
        Is better written as

        sub new { my $class = shift; bless \( my $scalar ), $class; }
        You don't normally get a blessed reference passed into new(). If you're using my $foo = Object::Foo->new, or something similar, its the name of the class (Object::Foo in my example) that's passed into new(). So, my $class = shift; is more logical.
      According to Wikipedia (and my personal understanding), cargo-cult programming is the superstitious inclusion of code that serves no purpose, merely for the sake of conforming to some remembered template. However, as you say in ref($proto) - just say no!:
      Rather, I'm saying that "new" on an instance could mean either "clone" or "make new one like...", and thus you are confusing at least half your audience, guaranteed.
      Thus you have already described at least two purposes of this construction. The fact that experienced OO programmers don't all agree on which purpose it should serve doesn't make the construct invalid—the idea that the existence of differing possible interpretations means bad code seems (to me) entirely counter to TIMTOWTDI.

      Anyway: even if this construction is a bad, or at least inelegant, idea (which I think that it is), I don't think that automatically makes it cargo-cult programming.

Re: To be, or not to be, a package? That is the question
by zentara (Cardinal) on Dec 04, 2008 at 13:50 UTC
    I believe your $value 's earlier declaration error, can be solved by ending each package with 1; ( a true value) to isolate the package.
    sub value { $value } 1; package SVC::DnsEntity::IPv4;

    I'm not really a human, but I play one on earth Remember How Lucky You Are
      zentara++ :-))

      TFT zentara, I knew it would be so obvious I couldn't see it.

      Thanx again

      Update

      Moderated zentaras kudos - when implemented, it didn't have quite the expected result...in fact, it didn't change it! :-(

      A user level that continues to overstate my experience :-))
        Doh...posted without testing. This works...wrap your packages in {}.
        { package whatever; 1; }

        I'm not really a human, but I play one on earth Remember How Lucky You Are
(OT) Re: To be, or not to be, a package? That is the question
by Lawliet (Curate) on Dec 04, 2008 at 20:35 UTC
    Once more I prostrate myself before you

    Sounds dirty :P

    I'm so adjective, I verb nouns!

    chomp; # nom nom nom