A recent thread discussed about the possibility to use something like global variables in Perl, and how to do it. I don't want to discuss the the pros and cons of global variables in this post (but everyone is encouraged to contribute with their point of view, of course), but only a little toy module that derived from that thread.

Using plain package variables is a way to gain global variables, because they're accessible program-wide:

package A; print "the global k in package B is $B::k\n";
That is, if we fully qualify a variable, including its package, we're always allowed to access it. And yes, this works under strict too. OTOH, this approach is pretty weak with respect to typos.

The fact is that this really sucks. We're all lazy, and we don't want all that typing. One interesting suggestion is to code a sub, that can be easily made really global, so that we can use it wherever we need (see here):

package main; my $MW = ...; # Create your main window here. sub UNIVERSAL::MW {$MW}
This really scares me a bit. This is really globally pervasive, and I fear (but it's my ignorance, for sure) that it could break when we're using some modules. For example: what happens when we use AUTOLOAD too? I really don't know, but I suspect that half of the audience won't be happy. Moreover, this approach is subject to typos as well, because the compiler will accept a call to a function pretty happily.

That's why I thought about creating something that could be used like this (from the SYNOPSIS of vars::global):

# In the place/package where we want to create globals use vars::global create => qw( $foo @bar %baz ); # Add some more global symbols vars::global->create(qw( $hello @world %now )); # Somewhere else, where we need to access those globals use vars::global qw( $foo @bar %baz ); # Don't try to use globals that do not exist use vars::global qw( $Foo ); # typo, croaks use vars::global qw( @inexistent ); # we don't create by default # use 'create' as above # You can also import and create new globals use vars::global qw( $foo %baz ), create => qw( $hey @joe ); # If you're lazy, you can import all the globals defined so far use vars::global ':all';
The rationales are the following: Here comes the implementation:
package vars::global; { use version; $VERSION = qv('0.0.1'); use warnings; use strict; use Carp; # Module implementation here # Where we keep existing globals my %ref_for; sub import { my $package = shift; my $caller = caller; my @import; GRAB_IMPORTS: while (@_) { my $param = shift; last GRAB_IMPORTS if lc($param) eq 'create'; if (lc($param) eq ':all') { @import = keys %ref_for; while (@_) { last GRAB_IMPORTS if shift eq 'create'; } last GRAB_IMPORTS; } push @import, $param; } $package->_create($caller, @_); $package->_import($caller, @import, @_); return; } ## end sub import sub create { my $package = shift; my $caller = caller; $package->_create($caller, @_); $package->_import($caller, @_); return; } ## end sub create sub has { my $package = shift; my ($symbol) = @_; return unless exists $ref_for{$symbol}; return $ref_for{$symbol}; } ## end sub has sub _create { my $package = shift; my $caller = shift; my @symbols = @_; no strict 'refs'; no warnings 'once'; foreach my $symbol (@symbols) { # Some checks croak "undefined symbol" unless defined $symbol; croak "empty symbol" unless length $symbol; my $identifier = substr $symbol, 1; croak "invalid identifier '$identifier'" unless $identifier =~ /\A \w+ \z/mxs; my $fqn = $package . '::' . $identifier; my $sigil = substr $symbol, 0, 1; $ref_for{$symbol} = $sigil eq '$' ? \${$fqn} : $sigil eq '@' ? \@{$fqn} : $sigil eq '%' ? \%{$fqn} : croak "invalid sigil: '$sigil'"; } ## end foreach my $symbol (@symbols) return; } ## end sub _create sub _import { my $package = shift; my $caller = shift; no strict 'refs'; foreach my $symbol (@_) { my $ref = $package->has($symbol) or croak "non existent global: '$symbol'"; *{$caller . '::' . substr $symbol, 1} = $ref; } return; } ## end sub _import } 1; # Magic true value required at end of module __END__
The "core" of the import is the following line:
*{$caller . '::' . substr $symbol, 1} = $ref;
which strips the sigil out of $symbol to get the variable identifier, prepends the package name (in $caller) with the colons and defines the symbol in the symbol table. Something basically equal to what happens in vars itself, but I have to credit MJD for having learnt it (e.g. here).

We keep a hash %ref_for to remember which global variables have been created so far. New items are added only when the create() method is called, either explicitly or using the create parameter in the import list. For this reason, the import() method first selects the symbols that have to be simply imported from those that require creation too, so that you can write something like:

use vars::global qw( $these @exists ), create => qw( $those @are %new );
One can check if a symbol already exists with the has() method, which returns a reference to the global variable (if this exists) or undef.

The two methods _import() and _create() factor out the code, so that we can call them from both import() and create(). They cannot call each other, because otherwise caller would be spoiled by the added call. Instead, we factor the code out, call caller from the "public" method (that without the underscore) and then pass this as a parameter to the "private" one (that with the underscore).

Conclusion: I had fun writing it... but shall I ever use it? I doubt :)

Flavio
perl -ple'$_=reverse' <<<ti.xittelop@oivalf

Don't fool yourself.

In reply to Global variables by polettix

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.