jacques has asked for the wisdom of the Perl Monks concerning the following question:

I have a hash that is passed by reference to a function.

In that function, the hash gets tied and modified. After the function returns, the original hash now has new values. It goes a little something like this:

my %hash; my $blah = function(\%hash); warn Dumper \%hash; # prints a bunch of values
This is what the function looks like... (note that I simplified the function and everything else for demo purposes)
sub function { my ($hash) = @_; tie %$hash, 'Apache::Session::File', undef, { Directory => $r->dir_config('blah'), LockDirectory => $r->dir_config('blah'), }; $hash->{auth}->{user} = "jacques" }
Okay, everything is working as it is suppose to be. But when I put that tie() into a separate module that returns the tied hash, then the original hash is not modified. This is a problem. Here's what the function looks like when using that module.
sub function { my ($hash) = @_; $hash = MyModule::tie_session(); $hash->{auth}->{user} = "jacques"; }
When putting the tie() in a module, the %hash will have no values when the function returns. It never gets modified outside of the function. How can I use the modular approach and have the original hash modified?

Here's what the module looks like:

Package MyModule; use Apache::Session::File; sub tie_session { my $hash = {}; eval { tie %$hash, 'Apache::Session::File',undef, {Directory => $r->dir_config('Blah'), LockDirectory => $r->dir_config('Blah'), }; }; return $@ if ($@); return $hash; } 1;

Replies are listed 'Best First'.
Re: References, hashes, and tie() -- oh my
by japhy (Canon) on Jun 11, 2005 at 01:54 UTC
    The problem is that when you have a reference, $foo, that points back to some other data structure, such as @bar or %blat, you can only affect that original data structure by dereferencing $foo:
    @bar = qw( one small step for man ); $foo = \@bar; $foo->[1] = "big"; print "@bar"; # one big step for man
    If, instead, you re-assign to $foo, you've overwritten the fact that $foo is a reference:
    @bar = qw( one small step for man ); $foo = \@bar; $foo = [qw( one big step for man )]; print "@bar"; # one small step for man
    So, in your second function, even though $hash is a reference to the hash you're passing the function, you then overwrite that variable entirely, destroying the reference (but certainly not the underlying data). Your module's function should take a reference as an argument:
    sub tie_session { my ($hash) = @_; eval { tie %$hash, 'Apache::Session::File', undef, { Directory => $r->dir_config('Blah'), LockDirectory => $r->dir_config('Blah'), }; }; return $@ if $@; return $hash; # not actually necessary, but it returns true }
    which would be called like so:
    sub function { my ($hash) = @_; MyModule::tie_session($hash); $hash->{auth}->{user} = "jacques"; }

    Jeff japhy Pinyan, P.L., P.M., P.O.D, X.S.: Perl, regex, and perl hacker
    How can we ever be the sold short or the cheated, we who for every service have long ago been overpaid? ~~ Meister Eckhart
Re: References, hashes, and tie() -- oh my
by Limbic~Region (Chancellor) on Jun 11, 2005 at 00:08 UTC
    jacques,
    In the first working example, a hash is passed to the sub by reference and so modifications to the underlying data structure are reflected after the sub exits. In the second broken example, the sub is creating a lexical hash reference that can't possibly affect a hash outside of its scope. The solution is to modify the module's sub to accept a hash reference.

    Cheers - L~R

    Incidently, my ($hash) = @_; is spelled better as my $hash = shift;
      my ($hash) = @_; is spelled better as my $hash = shift;
      Why? With the first way, if you change your mind about the number of params you can change just the left hand side, but with your way, if you change the left hand side and forget to change the right, you'll end up with a hard to spot bug
      Incidently, my ($hash) = @_; is spelled better as my $hash = shift;

      .. I couldn't disagree with this more....

      dEvNuL
        devnul,
        Not that it has anything to do with jacque's problem, but what specifically do you disagree with? That my ($hash) = @_; shouldn't be spelled differently at all, or the way I chose to spell it? The only way I can see to improve the readability would have been to say my $hash = shift @_;

        Update: Ok, after hearing several arguments in the CB along with jZed's reply, I am convinced that it is not necessarily better to write it that way. I doubt I will change how I write my code but I won't think it is deficient when others do.

        Cheers - L~R

Re: References, hashes, and tie() -- oh my
by jacques (Priest) on Jun 11, 2005 at 02:05 UTC
    Problem solved. Thank you very much, Limbic. (And happy anniversary!)

    Not only did I have to pass the hash as a reference to the module function, I also had to get rid of that return $hash and the assignment which redefines the $hash (japhy pointed this out, but Limbic talked to me before and then I have been sitting on this comment for like an hour without creating it). Thanks all.