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

Consider:
use strict; use warnings; require Data::Dumper; my $foo = { # blessed one => 1, two => 2, six => 6, }; bless $foo, q{Number::3ch}; my $bar = { # not blessed one => 1, two => 2, six => 6, }; sub foo { # pass by ref my $ref = shift; $ref->{one} = q{One}; $ref = {}; # why won't this replace the underlying hash? return $ref; } # try on blessed reference print Data::Dumper::Dumper($foo); foo $foo; print Data::Dumper::Dumper($foo); # try on unblessed reference print Data::Dumper::Dumper($bar); foo $bar; print Data::Dumper::Dumper($bar);
I was expecting the underlying anonymous hashes to be replaced, but they are not.

Results:

$VAR1 = bless( { 'six' => 6, 'two' => 2, 'one' => 1 }, 'Number::3ch' ); $VAR1 = bless( { 'six' => 6, 'two' => 2, 'one' => 'One' }, 'Number::3ch' ); $VAR1 = { 'six' => 6, 'one' => 1, 'two' => 2 }; $VAR1 = { 'six' => 6, 'one' => 'One', 'two' => 2 };
Questions:
  1. How do I make this DWIM?
  2. Why is this not DWIM?
thanks!

Replies are listed 'Best First'.
Re: unexpected behavior on hash passed by reference
by haukex (Archbishop) on Feb 26, 2022 at 18:40 UTC
    $ref = {}; # why won't this replace the underlying hash?

    $ref is a scalar that holds a reference to a hash. {} creates a new anonymous hash, and you're overwriting the hashref with a reference to the new anonymous hash. In other words, you're just changing which hash $ref points to. To clear the hash pointed to by the reference, you need to dereference it: %$ref = (); will clear the hash pointed to by $ref (without removing the blessing).

      Amazing, I never considered using a dereference on the LHS of the assignment. What if I wanted to also remove the blessing? Thanks!
        What if I wanted to also remove the blessing?

        Sounds like an XY problem and/or that your example isn't really representative - why do you want to do this?

        Anyway, TIMTOWTDI:

        use warnings; use strict; use Data::Dump; sub one { $_[0] = {abc=>'one'}; } my $r1 = bless {r=>111}, 'One'; dd $r1; # bless({ r => 111 }, "One") one $r1; dd $r1; # { abc => "one" } sub two { my $ref = shift; $$ref = {abc=>'two'}; } my $r2 = bless {r=>222}, 'Two'; dd $r2; # bless({ r => 222 }, "Two") two \$r2; dd $r2; # { abc => "two" } sub three { my $ref = shift; return {abc=>'three'}; } my $r3 = bless {r=>333}, 'Three'; dd $r3; # bless({ r => 333 }, "Three") $r3 = three($r3); dd $r3; # { abc => "three" }

        And there's stuff like Acme::Damn, but I'm fairly certain that wouldn't be the right solution.

        > What if I wanted to also remove the blessing?

        I'm fairly sure there is some perldoc where Larry explains that - contrary to untie - he never saw any sense in implementing unbless ...

        Anyway you are always free to re-bless any object into a dummy class.

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery

Re: unexpected behavior on hash passed by reference
by LanX (Saint) on Feb 27, 2022 at 00:29 UTC
    >
    sub foo { # pass by ref my $ref = shift; $ref->{one} = q{One}; $ref = {}; # why won't this replace the underlying hash? return $ref; }

    I think your confusion comes from thinking you are doing a pass by reference like commented with # pass by ref

    But your code $ref = shift; is literally a pass by value , $ref will be the copy of a (Perl) reference.

    For a pass-by-reference you have to replace $ref with $_[0] , which is an "alias" of the original argument $foo.

    Hence will $_[0] = {}; also replace the original hash.

    I know it's confusing but "reference" in CS lingo is NOT a reference in Perl lingo, but best translated as "alias".

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery