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

perldoc perlsub states:
sub maybeset { my($key, $value) = @_; $Foo{$key} = $value unless $Foo{$key}; }
Because the assignment copies the values, this also has the effect of turning call-by-reference into call-by-value. Otherwise a function is free to do in-place modifications of "@_" and change its caller's values.

But if this is the case, then how come

sub XXX { my ($self) = @_; $self->{age} = '17'; }
actually updates the anonymous hashref that was passed in? Supposedly the assignment changed call by reference to call by value and hence any edits to $self should not have affected the caller.

I do this all the time and it just struck me that it shouldn't work.

Replies are listed 'Best First'.
Re: How does my $self = (@_) actually work?
by chipmunk (Parson) on Aug 04, 2001 at 02:05 UTC
    Of course it should work, because $self is a reference. Although you copied the reference from @_ to $self, you didn't copy the hash that it refers to. So when you change something via the reference, you change the original hash.

    If it's just a one-level hash, you could copy it like this:

    my %copy = %{ $self }; # or my $copy = { %{$self} };

    If you want to copy a nested data structure, check out How do I print out or copy a recursive data structure? in the perlfaq.

Re: How does my $self = (@_) actually work?
by maverick (Curate) on Aug 04, 2001 at 02:10 UTC
    because ($self) = @_; makes a copy of the reference not a copy of the thing(s) the reference points at.

    consider this

    sub pass_var { my ($foo,$bar) = @_; print "foo: $foo bar: $bar\n"; $foo = 'c1'; $bar = 'c2'; print "changed: foo: $foo bar: $bar\n"; } sub pass_ref { my ($hash) = @_; print "hash: $hash->{'key'}\n"; $hash->{'key'} = 'c1'; print "changed: hash: $hash->{'key'}\n"; print "hashref is: $hash\n"; } my $hash; my $foo; my $bar; $foo = 1; $bar = 2; $hash = {'key' => 1 }; &pass_var($foo,$bar); print "after: foo: $foo bar: $bar\n"; &pass_ref($hash); print "after: hash: $hash->{'key'}\n"; print "hashref is: $hash\n";
    which yields this output
    foo: 1 bar: 2 changed: foo: c1 bar: c2 after: foo: 1 bar: 2 hash: 1 changed: hash: c1 hashref is: HASH(0x80f86e0) after: hash: c1 hashref is: HASH(0x80f86e0)
    See how the hash both in and out of the sub point to the same memory address?

    /\/\averick

(Ovid) Re: How does my $self = (@_) actually work?
by Ovid (Cardinal) on Aug 04, 2001 at 02:12 UTC

    One of the features of Perl is that the special variable @_ is actually an alias of the arguments passed to it. In other words, the following code may confuse people:

    my $x = 3; somesub ( $x ); sub somesub { print ++$_[0]; } print $x;

    Because of the aliasing, &somesub actually alters the value of $x and both print statements print 4. However, with your second example, whatever you pass to &XXX must be a reference to an anonymous hash. When $self is assigned the value of $_[0], it is assigned the reference and thus can alter the underlying anonymous hash values. You can copy references all day long and they still point to the same thing (so long as you don't accidentally stringify them).

    Cheers,
    Ovid

    Vote for paco!

    Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

Re: How does my $self = (@_) actually work?
by Zaxo (Archbishop) on Aug 04, 2001 at 02:29 UTC

    If you pass in a hashref as the first argument, $_[0] refers to aliases that scalar. The lexically scoped copy produces a second hashref to the same external hash.

    Some variations:

    sub XXX { my ($self) = @_; $self->{age} = '17'; # does as you say $_[0]->{'age'} = '21'; # a miraculous drivers license $self = { %{$self} }; # now we have a copy of the innards $self->{age} = '17'; # restoration of youth # the world now thinks age=>21, we have private age=>17 # Youth will be lost when the sub exits }

    Hope that helps.

    After Compline,
    Zaxo

    Update: Ovid uses 'alias', and it's a better term for it. Less chance of confusion.

Re: How does my $self = (@_) actually work?
by tadman (Prior) on Aug 04, 2001 at 08:09 UTC
    It isn't unusual to see something like my ($self) = @_;, but I've found that using my $self = shift; is more flexible. After all, if you need this in one function, you need it in every one, so it is easy to cut-and-paste.

    That being said, the reason you are able to modify "$self" is because you are not really modifying it. You are modifying the data that it "refers" to. It's a reference, which is really like a pointer. It merely indicates where data is stored, instead of actually storing data. This is why you need to use the arrow operator to "dereference" it and get the goods.

    So, if you had code like:
    sub X { my $self = shift; $self->{age} = 17; }
    Then, as you expect, the 'age' value of your $self-referenced object becomes 17. If you did something like this:
    sub X { my $self = shift; print "\$self = $self\n"; $self->{age} = 17; print "\$self = $self\n"; }
    You will note that $self does not change in the function, and that in both cases it is clearly a reference to a HASH.

    This is something different:
    sub Z { my ($foo) = @_; print "\$foo = $foo\n"; $foo = 17; print "\$foo = $foo\n"; } my $bar = 29; print "\$bar = $bar\n"; Z($bar); print "\$bar = $bar\n";
    In this case, $bar is not modified, though a copy of it, called $foo, is. Here's an example that uses a reference instead:
    sub Z { my ($foo) = @_; print "\$foo = $foo\n"; $$foo = 17; print "\$foo = $foo\n"; } my $bar = 29; print "\$bar = $bar\n"; Z(\$bar); print "\$bar = $bar\n";
    On the way into the function, the variable is prefixed with a backslash, meaning "pass by reference". Inside the function, the scalar is "used" with a second $, meaning that you are modifying the value, not the reference. This is another form of dereferencing, not unlike the "arrow operator" used by arrays and hashes.

    Any time you see an arrow operator, think of it like a pointer, which it is supposed to represent visually. It points to something which is elsewhere.