0

I have one script (A.pl) and one package (B.pm), I want to create a static variable in B.pm so that it can accessible to A.pl.

A.pl

use lib 'path/to/B_Package';

for loop 10 times {
  fun(age);
}
if ($count>0) {
  print "script fails";
}

B.pm

package B {
  fun() {
    my $age_given = shift;
    my $count;

    eval {
      result = someFileHandling;
    } or die {
      $count++; 
    }
  }
}
brian d foy
  • 129,424
  • 31
  • 207
  • 592
  • You want to check the value of the `$count` variable in module B from `A.pl`? If you declare the variable as `our` at package scope instead of `my` inside the function it will be accessible – Håkon Hægland Jun 09 '20 at 20:16
  • 2
    Tip: There's a module called `B`. Use a different name for your module. – ikegami Jun 10 '20 at 03:07

2 Answers2

7

I'd question such design, and some alternatives are offered below.

But yes it can be done -- a variable declared as our can be accessed by its fully qualified name.

In the package file Pack.pm

package Pack;    
use warnings;
use strict;

use Exporter qw(import);
our @EXPORT_OK = qw(func);

our $count = 7;

sub func { ++$count }

1;

and in its user

use warnings;
use strict;
use feature 'say';

use Pack qw(func);

for (1..2) {
    func();
    say "Current value of a global in 'Pack': ", $Pack::count;
}

$Pack::count = 123;
say "Current value of a global in 'Pack': ", $Pack::count;

So changes to $count made in Pack::func() are seen in the calling program. More to the point, $Pack::count can be directly written by any code in the interpreter.

The package globals that are meant to be used directly, like $count above, are tricky creatures that can be hard to use sensibly but are very easy to end up abusing.

In general you don't want them: their use runs contrary to the critical idea of dividing software in components that communicate via clearly defined interface, they introduce uncontrolled coupling and thus defeat scope, etc. With such variables in use distinct components in the code get entangled.

But they can of course be useful and are used in libraries, mostly for constants and parameters.

Now, having them change as well? That can get out of control, and even though that, too, is used in libraries (to control their behavior by setting parameters) it veers closer to an analogue of a "God Class," an all-controlling entity. At that point I would flatly call it flawed and a trouble-maker.

Why not have subs handle the counting and return updated values? They can keep values using state pragma for instance. Or even using a file-scoped variable, as long as that is internal to its business and outsiders aren't allowed to poke at it.

Here's a sample for the two mentioned approaches in revised Pack.pm

package Pack;    
use warnings;
use strict;
use feature qw(state);

use Exporter qw(import);
our @EXPORT_OK = qw(count func1 func2);

my $count = 7;

sub func1 { ++$count }  # sets counter while doing its other work

sub count {                   # add check that input is numeric
    $count += shift  for @_;  # Set count if values passed,
    return $count;            # return value in either case
}

sub func2 {                       
    state $count = 0;             # keeps count (its own $count)
    return $count += shift // 1;  # demo: add some and return
}

1;

Demo for its use:

use warnings;
use strict;
use feature 'say'; 

use Pack qw(count func1 func2);

say "Call func2(), using 'state' feature, with its own counter: ";
for (1..2) { say "func2($_): ", func2($_) }
say '';

say "Set value for a file-wide counter, retrieve using count(): ";
for (1..2) { func1() }    
say "Count is: ", count();

say "Use count() to set values as well: ";
for (1..2) {  say "For #$_: ", count($_) }

This prints

Call func2(), using 'state' feature, with its own counter: 
func2(1): 1
func2(2): 3

Set value for a file-wide counter, retrieve using count(): 
Count is: 9
Use count() to set values as well: 
With 1: 10
With 2: 12

The next step up is to make this a class, and then you can implement any and all kinds of counters in very natural ways.

For more on variables, see this post and this post and this Effective Perler article, for starters.


An our variable is strictly speaking not a global, but a lexical that is aliased to a package variable (a "true" global) with the same name.

zdim
  • 64,580
  • 5
  • 52
  • 81
4

I think there's a better way to do what I'm guessing that you want to do. I think that you want to try something a certain number of times and give up if you can't acheive that goal.

When you call your subroutine, you want it to know how many times to try. Also, you want to know when it fails.

You don't need to share a variable for this. The die is going to take care of that for you. Call the sub as many times as you like, and each time you don't get back a value from eval, count that as an error:

my $errors = 0;
foreach ( 1 .. 10 ) {
    my $result = eval { do_this($age) };
    $errors++ unless defined $result;
    }
    
print "Script fails" if $errors > 0;

In the subroutine, you don't need to worry about how many times this has been done because that's happening at the higher level for you. You look at the result of the subroutine to decide if it failed and adjust a counter at the higher level. Now the subroutine can focus on it's small part instead of thinking about why you are calling it. You also don't need the eval at this level because you already have it at the higher level.

sub do_this {
    my( $age ) = @_;
    ... some file handling ...
    }

Factories

But let's say that there is some good reason for a lower subroutine to know its count. I don't want to pollute that subroutine for everyone—suppose that 10 other places in the program also call this subroutine and they all fail. Should that count against your call? You probably don't want that. But, there's a way around this. You can create a new version of the subroutine when you need to. A factory is a subroutine that makes other subroutines.

Let's say you want to try something a certain number of times. But, you might want to do that multiple times too. Make a new subroutine every time that you want to try this. Tell that subroutine how many tries it gets:

sub some_factory {
    my( $max_tries ) = @_;
    
    sub anon_thingy {
        my( $age ) = @_;
        for ( 1 .. $max_tries ) {
            ... file handling ... or die ...
            }
        }
    }
    

Your program would then look something like:

my $try_this = some_factory( 10 );

my $result = eval { $try_this->($age) };
print "Script fails" unless defined $result;

In the same program, you can do it again, and each generated code reference tracks its own use and doesn't bother other subs:

foreach $age ( list_of_ages() ) {
    my $new_sub = some_factory( 10 );
    my $result = eval { $new_sub->($age) };
    print "Script fails" unless defined $result;
    }

I spend quite a bit of time on this sort of stuff in Intermediate Perl and Mastering Perl.

brian d foy
  • 129,424
  • 31
  • 207
  • 592