=head1 NAME

Tie::WarnGlobal - Perl extension to aid in eliminating globals

=head1 SYNOPSIS

  use Tie::WarnGlobal::Scalar;
  tie $GLOBAL, 'Tie::WarnGlobal::Scalar', { get => \&get_global };

=head1 DESCRIPTION

A program full of global variables can be a mind-bending thing to debug. They come
in various shapes and sizes. Some are package variables exported by default. Some are
accessed directly using the '$Modulename::Global' syntax.

Most experienced programmers agree that using global variables is a
Bad Thing in large programs, since they both add to the complexity of
the program and can introduce subtle bugs. Globals introduce
complexity because they increase the number of things that you need to
keep in your head at a given time. ("What the heck is $FROBOZZ
supposed to be again?")  They can introduce subtle bugs because if one
piece of code accidentally says 

    if ($THNEE = 34)

when they meant to say

    if ($THNEE == 34)

then another piece of code miles distant
could break, and you'll have to hunt through the entire program for
the bug. This can be very time-consuming!

The standard remedy for rampant global variables is to write subroutines that
return the information that the global is supposed to contain. For example,
if you have a global like so:

    $CLOWN = "Bozo";

then you can instead write a subroutine like this one:

    sub get_clown { return "Bozo"; }

and replace all instances of $CLOWN with get_clown(). If at some point in
the program we change to a different clown, you can write a set_clown()
method and change any '$CLOWN = "Binky"' statements to 'set_clown("Binky")'.
For the curious, one way of doing this would be:

 BEGIN {

   my $clown = 'Bozo';

   sub get_clown () {
     return $clown;
   }

   sub set_clown {
     my ($new_clown) = @_;
     $clown = $new_clown;
   }

 }

Writing a 'set' function reintroduces some of the problems of having
the global variable in the first place, since calling set_clown() in
one part of the program can cause problems in a different piece of
code. However, you have accomplished several good things:

=over 4

=item * A 'set_clown' call is easier to spot than a '$CLOWN = foo' statement.

=item *
You can put access controls into set_clown() to make sure that get_clown() will always
return a valid clown.

=item *
It becomes easier to make get_clown() and set_clown() into class methods. Then you could have
calls like $circus->get_clown(), making it easier to separate circus-related stuff from non-circus stuff.

=back

Globals are elusive things. If you inherit (or write) a program with
all kinds of exported-by-default package variables, it can be hard to find them,
and time-consuming to replace them all at once.

Tie::WarnGlobal::Scalar is a partial answer. Once you've written a
routine that returns the value that was originally in your global
variable, you can tie that variable to the function, and the variable
will always return the value of the function. This can be valuable
while testing, since it serves to verify that you've written your new
'get'-function correctly.

In order to trace down uses of the given global,
Tie::WarnGlobal::Scalar can provide warnings whenever the global is
accessed. These warnings are on by default; they are controlled by the
'warn' parameter. Also, one can turn warnings on and off with the
warn() method on the tied object. If 'die_on_write' is set,
Tie::WarnGlobal::Scalar will die if an attempt is made to write to a
value with no 'set' method defined. (Otherwise, the 'set' method will
produce a warning, but will have no affect on the value.)

=head1 AUTHOR

Stephen Nelson, stephen@artmachine.com

=head1 SEE ALSO

perl(1), Tie::Watch(3), Memoize(3).

=cut

