package Variable::Alias; {
    use 5.6.0;
    use strict;
    use warnings;
    
    use Exporter;
    use Switch 'Perl6';
    use Carp 'croak';
    
    our @ISA=qw(Exporter);
    our @EXPORT_OK=qw(alias alias_s alias_a alias_h alias_r);
    our $VERSION=0.01;
    
    sub export_fail {
        #This can only be called because of an
        # insufficient version for alias();
        
        die sprintf("Perl version %vd doesn't support features needed for alias()", $^V);
    }
    
    sub alias_r {
        my($src, $dest)=@_;
        
        croak "Alias must be of the same type as the original"
            unless ref $dest eq ref $src;
        
        given(ref $dest) {
            when('SCALAR') {
                tie($$dest, 'Variable::Alias::Scalar', $src);
            }
            
            when('ARRAY') {
                tie(@$dest, 'Variable::Alias::Array', $src);
            }
            
            when('HASH') {
                tie(%$dest, 'Variable::Alias::Hash', $src);
            }
            
            else {
                croak "Can't alias type ", ref $src;
            }
        }
    }
    
    sub alias_s(\$\$) {
        goto &alias_r;
    }
    
    sub alias_a(\@\@) {
        goto &alias_r;
    }
    
    sub alias_h(\%\%) {
        goto &alias_r;
    }
    
    if($^V ge 5.8.0) {
        eval q{
            sub alias(\[$@%]\[$@%]) {
                goto &alias_r;
            }
        }
    }
    else {
        our @EXPORT_FAIL=qw(alias);
    }
}

package Variable::Alias::Scalar; {
    use strict;
    use warnings;
    
    use Tie::Scalar;
    our @ISA=qw(Tie::StdScalar);
    
    sub TIESCALAR {
        return bless $_[1], $_[0];
    }
}

package Variable::Alias::Array; {
    use strict;
    use warnings;
    
    use Tie::Array;
    our @ISA=qw(Tie::StdArray);
    
    sub TIEARRAY {
        return bless $_[1], $_[0];
    }
}

package Variable::Alias::Hash; {
    use strict;
    use warnings;
    
    use Tie::Hash;
    our @ISA=qw(Tie::StdHash);
    
    sub TIEHASH {
        return bless $_[1], $_[0];
    }
}

=head1 TITLE

Variable::Alias - Alias any variable to any other variable

=head1 SYNOPSIS

    use Variable::Alias 'alias';
    my $src;
    my $a;
    our $b;
    my @c;
    our @d;

    alias $src => $a;
    alias $a => $b;
    alias $b => $c[0];
    alias @c => @d;

    $src='src';
    # All the other variables now have the string
    # 'src' in the appropriate places.

=head1 DESCRIPTION

There are various ways to alias one variable to another in Perl.  The most
popular is by assigning to typeglobs.  This is quite efective, but only 
works with globals.  Another method is to use a module like 
C<Lexical::Alias> or C<Devel::LexAlias>, but as their names suggest, these 
only work with lexicals.  There's no way to alias an element of an array
or hash.

C<Variable::Alias> changes all that.  It uses a tie to provide One True Way
to alias a variable.

=head2 Interface

C<Variable::Alias> may export any or all of five functions.  If you've used
C<Lexical::Alias>, the interface is virtually identical.

=over 4

=item C<alias>(VAR, VAR)

C<alias> takes two variables of any type (scalar, array or hash) and 
aliases them.  Make sure they have the sigil you want on the front.

This function is only available in Perl 5.8.0 and later, because the
prototype tricks it uses were first implemented in that version.

=item C<alias_s>(SCALAR, SCALAR)

C<alias_s> takes two scalars and aliases them.

=item C<alias_a>(ARRAY, ARRAY)

C<alias_a> takes two arrays and aliases them.  Note that this is actual
I<arrays>, not array I<elements>, although you can alias references in
elements, like so:

    alias_a(@short, @{$some->sequence->{of}->calls->{that's}[2]{long}});

=item C<alias_h>(HASH, HASH)

C<alias_h> takes two hashes and aliases them.

=item C<alias_r>(REF, REF)

C<alias_r> takes two references and aliases them.  The referents must be
of the same type.

=back

=head2 Breaking aliases

If at some point you want to break the alias, just say C<untie $variable>.
(The variable will not retain the value it had while aliased.)

=head1 DIAGNOSTICS

=over 4

=item C<Alias must be of the same type as the original>

You tried to alias a variable of one type to a variable of another (e.g.
C<alias($scalar, @array)>).  You can't I<do> that.

=item C<Can't alias type %s>

You tried to alias a subroutine or a glob or something.  This module only
handles scalars, arrays, and hashes; everything else you might want to 
alias lives in a typeglob anyway.

=item C<Type of arg %s to %s must be %s (not %s)>

This mouthful is generated by Perl; it means that you used e.g. a scalar 
with C<alias_a>.  Make sure you're using the right C<alias_X> variant for 
the type you're aliasing, and that C<Variable::Alias> can actually handle
the type in question.

=back

=head1 SEE ALSO

L<perldata/Typeglobs and Filehandles>, C<Devel::LexAlias>, and
C<Lexical::Alias> for information on other aliasing methods.

L<perltie> for information on tying.

=head1 BUGS

This'll be slow, because tied variables always are.  Blech.  If your 
aliasing needs are fairly simple, consider just using C<Lexical::Alias>
or typeglobs--they'll be faster.

If you find any other bugs, drop me a note at <brentdax@cpan.org>.

=head1 AUTHOR

Brent Dax <brentdax@cpan.org>

=head1 COPYRIGHT

Copyright (C) 2002 Brent Dax <brentdax@cpan.org>.  All Rights Reserved.

This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
