in reply to Re^2: Use of uninitialized variables?
in thread Use of uninitialized variables?

Wups, mybad on the terminology. I didn't know what to call them but global doesn't seem right either. *shrug* As far as these variables go, my limits the scope in such a way that in a different module the variable isn't available. Try separating the packages into their own modules, like in this example:

Mytest.pm:

package Mytest; use strict; use warnings; my $var = 1; 1;
And then a user of the module:
use strict; use warnings; use Mytest; print $Mytest::var . "\n";
You'll get this:
Name "Mytest::var" used only once: possible typo at usemytest.pl line 5.
Use of uninitialized value in concatenation (.) or string at usemytest.pl line 5.
Or this:
use strict; use warnings; use Mytest; print $var;
You'll get this:
Global symbol "$var" requires explicit package name at usemytest.pl line 5.
Execution of usemytest.pl aborted due to compilation errors.
Without strict and warnings:
use Mytest; print $var;
or
use Mytest; print $Mytest::var;
You get nothing; $var is completely invisible outside it's module...or am I missing something? :/ Is there another way to access that scalar that I'm missing?

As far as the example goes, it is an overly simplified demonstration of some interactions between many modules in a medium sized tool I'm working on. Here's a slightly more concrete usage, and two instances where the issue happens. p1 shows the same kind of issue as before: by assigning $undef to the variable on the same line it's created, if used later in a begin block, then the undef overrides anything done during the begin block. p2 shows the issue in a much more real world scenario: you have a simple inside out class, and decide to 'initialize' the "private variable" hashes. For that example, I'm trying it three different ways: initializing in line, using the default 'my' behavior, and initializing in a begin block, to show what happens in each of these scenerios:

#!/usr/bin/perl -w package Problem; use strict; use warnings; use Scalar::Util qw(refaddr); { my %key1_of; # leave blank test my %key2_of = (); # initialize test my %key3_of; BEGIN { %key3_of = (); # initialize with a BEGIN } sub new { my ($class, $ar1, $ar2, $ar3) = @_; my $new_object = bless \do{my $anon_scalar}, $class; $key1_of{refaddr($new_object)} = $ar1; $key2_of{refaddr($new_object)} = $ar2; $key3_of{refaddr($new_object)} = $ar3; return $new_object; } sub get_key1 { my $self = shift; return $key1_of{refaddr($self)}; } sub get_key2 { my $self = shift; return $key2_of{refaddr($self)}; } sub get_key3 { my $self = shift; return $key3_of{refaddr($self)}; } } 1; # Here's a user of the above object. package Main; use strict; use warnings; # Below: Creating values for the sake of showing the issue at hand... # First variable: assigning undef (declaring its initial value), # but due to a subroutine or other complex series of processing, it # gets assigned a value during a BEGIN block my $p1 = undef; # using the 'always assign a value' paradigm here my $p2; # leaving it blank for now my $p3 = undef; # same 'always assign' paradigm here, but not used i +n 'begin' # some processing happens in here; for example, let's say these variab +les are # dependent on user input or automatic config files, so $p1, $p2, $p3 +might # not ever be used. It just turns out that this time, they all are. # However, the user of the package creates these instances of the obje +ct in a # begin block, and for whatever reason, the user can't avoid doing thi +s. BEGIN { $p1 = Problem->new('P11', 'P12', 'P13'); $p2 = Problem->new('P21', 'P22', 'P23'); } $p3 = Problem->new('P31', 'P32', 'P33'); sub printit # for display purposes { my $p = shift; if (defined $p) { print " k1: " . $p->get_key1() . "\n"; my $k2 = $p->get_key2(); $k2 = "(undef)" if ! defined $k2; print " k2: $k2\n"; print " k3: " . $p->get_key3() . "\n"; } else { print " Error: Object is undefined.\n"; } }; print "P1:\n"; printit($p1); print " (Due to scoping of the undef assignment, the object itself i +s lost.)\n"; print "P2:\n"; printit($p2); print " (Note loss of key 2's value; it's lost because %key2_of\n" . " is assigned to () after the object's creation).\n"; print "P3:\n"; printit($p3); print " (As expected, no issues at all).\n";
And then, the output is:
P1:
   Error: Object is undefined.
   (Due to scoping of the undef assignment, the object itself is lost.)
P2:
   k1: P21

   k2: (undef)
   k3: P23
   (Note loss of key 2's value; it's lost because %key2_of
    is assigned to () after the object's creation).
P3:
   k1: P31
   k2: P32
   k3: P33
   (As expected, no issues at all).
As you can see, p1 is completely wiped out by the initial assignment and p2 loses it's member variable due to the assignment of () to it after the object is created in the BEGIN block. Setting up the variable in a begin block ie, initializing it to 'empty', doesn't cause any problems for users, no matter where they use it.

The issue here is if we're initializing a value, and the initial value is empty or undef, why do it twice? It simply makes the initialization process even more complex. If we're not using it or not using it right away, we might as well take advantage of the behavior for perl to automatically setting scalars to undef or arrays/hashes to empty.

Or am I just completely off about all this?

Replies are listed 'Best First'.
Re^4: Use of uninitialized variables?
by kyle (Abbot) on Jun 12, 2008 at 17:29 UTC

    Your initial series of examples essentially just demonstrate the difference between lexical variables and package variables that have been brought up in the thread already.

    The larger example is more interesting and certainly makes a more coherent case against initialization. Thanks.

Re^4: Use of uninitialized variables?
by ikegami (Patriarch) on Jun 12, 2008 at 18:00 UTC

    As far as these variables go, my limits the scope in such a way that in a different module the variable isn't available.

    No one's disputing that.

    Here's a slightly more concrete usage, and two instances where the issue happens

    You're creating instances of a class before the class's module has been executed, and you're blaming the module? Change

    ... package Problem; { ... 1; } package Main; ...

    to

    ... BEGIN { package Problem; ... } ...

    or move the code to a used file. use adds the BEGIN for you.

    (By the way, the "1;" is useless, and you misspelled "package main;". And note how "package main;" is no longer needed when you move the package statement into the curlies.)

    As for the second issue,

    my $p1 = undef; BEGIN { $p1 = Problem->new('P11', 'P12', 'P13'); }

    I've already stated I think you're obviously doing something wrong if you're initializing something twice.

      I've already stated I think you're obviously doing something wrong if you're initializing something twice.
      We're in violent agreement that you shouldn't initialize something twice; this is simply an illustrative example of what happens when you do, as is being recommended to me. It *looks* like undef or () is being assigned right away, but it's not, depending on how the module is used. The assignment at a later point is supposed to be the initial value, NOT the value on the same line. By assigning early you can cause problems, which is the point of the example above. Initializing these values that you expect to initialize later is a completely superfluous and potentially (as shown in the above example) disastrous process.

        as is being recommended to me

        What the OP quoted wasn't nearly that specific. Keep in mind the goal of an idiom or practice when applying it. In this case, it's for "the readers of the code can immediately see that the lack of definition is intentional". That means that

        my $var; BEGIN { $var = ...; }

        satisfies the requirements. You can even put those two statements on the same line. I provided an example above of what would satisfy the practice if you had some complex initialization code in a previous post in this thread.

        You always have to be very careful when using BEGIN blocks to make sure all the code that should be in a BEGIN block is in a BEGIN block. You've shown an example of this already when the inline module that did everything right was messed up by a BEGIN block in its user.

        Basically, what I'm saying is you can't change

        my $var = "some value"; ... code that uses $var ...
        to
        my $var = "some value"; BEGIN { ... code that uses $var ... }
        You need to change it to
        my $var; BEGIN { $var = "some value"; ... code that uses $var ... }

        If you're particularly "picky", you could use

        my $var; BEGIN { $var = "some value"; } BEGIN { ... code that uses for $var ... }

        It has nothing to do with whether "some value" is there because PBP recommended it, or because it needs to be there for the code to function. I don't agree with the practice the OP quoted, but badly applying BEGIN blocks is not an argument against the practice.

Re^4: Use of uninitialized variables?
by j1n3l0 (Friar) on Jun 13, 2008 at 09:24 UTC
    First off, there is a difference between a package variable and a global variable. A package variable is accessible from outside the package, you just have to completely qualify the variable name. A global variable just means a variable visible everywhere within the current scope ... which means a variable declared at the start of a file (or package) is visible throughout that file.

    Here's an example:

    #!/usr/bin/env perl use 5.010_000; use strict; use warnings; { package MyTest; my $global = 'global variable'; our $package = 'package variable'; sub access_global { return $global } 1; } { package main; my $package_var = $MyTest::package; my $global_var = MyTest::access_global(); my $warning_var = eval { $MyTest::global }; say ">$_<" for $package_var, $global_var, $warning_var; } __END__ >package variable< >global variable< >< Name "MyTest::global" used only once: possible typo at /Users/io1/Work +space/example/package_variables.pl line 16. Use of uninitialized value $_ in concatenation (.) or string at /Users +/io1/Workspace/example/package_variables.pl line 18.

    To get at a variable that is global within a package "MyTest" from outside "MyTest" you have to use a subroutine access_global()

    As for the rest of your example, I suggest you use Class::Std to implement your inside-out class. There is a BUILD() method that you can use to initialize variables in non-standard ways.

    Hope that helps =)


    Smoothie, smoothie, hundre prosent naturlig!