skip to Main Content

I haven’t coded anything in Perl for a long time. Now I have started again and came across an interesting situation when coding two packages within a single ‘.pm’ file. Until now I thought that if I define two packages in a file (e.g. ConfigData and RuntimeData), I can define a variable (declared with ‘my’) with the same name (e.g. my $instance) for each package without conflicts. As an example, here is a minimal code to illustrate this:

package Jabs::ConfigData;
use strict;
use warnings;

my $instance;
sub new {
        my ($class) = @_;
        unless (defined $instance) {
                $instance = bless {}, $class;
        }
        return $instance;
}
1;  # End of 'ConfigData'

package Jabs::RuntimeData;
use strict;
use warnings;

my $instance;
sub new {
        my ($class) = @_;
        unless (defined $instance) {
                $instance = bless {}, $class;
        }
        return $instance;
}
1;  # End of 'RuntimeData'

I called perl -c test2.pm and got the following message as feedback:

"my" variable $instance masks earlier declaration in same scope at test2.pm line 27.
test2.pm syntax OK

Now it is not clear to me why the scope should be the same. Because ‘my $instance is defined in two different packages.
I am using Ubuntu 24.04 LTS and Perl5 (revision 5 version 38 subversion 2)

Can someone perhaps provide me with an explanation for my comprehension problem?

2

Answers


  1. The related perldoc -f my says:

    A my declares the listed variables to be local (lexically) to the enclosing block, file, or eval.

    The package keyword in the form of package NAME does not include such a scope. Although the documentation for package mentions you can declare a package with a block, e.g. package NAME BLOCK. At the bottom is my demonstration of such blocks.

    perldoc perlmod elaborates:

    A package statement affects only dynamic global symbols, including subroutine names, and variables you’ve used local() on, but not lexical variables created with my(), our() or state().


    use strict;
    use warnings;
    use feature 'say';
    
    package Conf {  # <-- block start
        use strict;
        use warnings;
        
        my $instance;
        sub new {
                my ($class) = @_;
                unless (defined $instance) {
                        $instance = bless {}, $class;
                }
                $instance->{foo} = "Confs";
                return $instance;
        }
        1;
    } # <-- block end END OF CONF
    
    package eData { # <-- block start
        use strict;
        use warnings;
        
        my $instance;
        sub new {
            my ($class) = @_;
            unless (defined $instance) {
                    $instance = bless {}, $class;
            }
            $instance->{foo} = "eDatas";
            return $instance;
        }
        1;
    } # <-- block end END OF EDATA
    
    my $c1 = Conf->new;    # loads the first $instance 
    my $c2 = eData->new;   # loads the other $instance, different variable and scope
    
    say $c1->{foo};
    say $c2->{foo};
    

    Outputs:

    Confs
    eDatas
    
    Login or Signup to reply.
  2. The short answer is that the package variables and lexical variables are two separate variable tracking systems that have nothing to do with each other.

    A package variable is effectively global. It exists at all times and places in the program as long as you know the fully-qualified package name (such as main::foo). The package statement merely changes the default package to use. The local temporarily replaces the value of package variable in a scope (containing block or file).

    The lexical variables are completely controlled by scope (containing block or file), that contains the declaration (my).

    In your case, both instances of my $instance appear in the same scope (the file).

    But, it looks like you are trying to create two singleton classes where you either return the thing you already created or create it on first use.

    Perl v5.10’s state lets you move that variable into the subroutine. That’s a persistent lexical variable. And, the // (defined-or) let’s you use a binary assignment to make things simple:

    use v5.12;
    
    package Foo { # block package needs v5.12, not necessary for problem
        sub new {
                state $instance; # needs v5.10;
                return $instance //= bless {}, $_[0];
        }
    }
    

    You can do the same thing prior to v5.10 by defining your new inside a scope that contains the $instance. Now that $instance only exists in that scope:

    
    package Foo;
        {
        my $instance;
        sub new {
                return $instance if defined $instance;
                $instance = bless {}, $_[0];
            }
        }
    
    package main;
    
    ...
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search