In the CB earlier today an aspirant asked how a call to an unrelated constructor could be clobbering an existing object. After the usual round of questions, answers and red herrings a little code was posted to a scratch pad. The key elements were:

my $self = bless {}, $class; ... my $self->{agent} = WWW::Mechanize->new ();

Of course presented like that the problem is immediately obvious, but the reason may not be.

So, in the style of Hofstadter's 'Gödel, Escher, Bach: an Eternal Golden Braid' I offer the following conversation (with names changed to protect ... whatever). We pick up the CB chat from the point where there was some runable code:

Zeno: Ah. Running the script with -w (resp. use warnings), it tells me that my $self->{agent} = is redefining the my $self; variable

Tortoise: warnings are your friend.

Achilles: In this case strict does the trick

Tortoise: Tortoise lost out to Zeno again, but only because he had to ssh to somewhere with www::mech installed

Tortoise: A: it does?!

Zeno: Achilles: No, that code is strict safe it seems

Tortoise: the code has strict

Achilles: I guess it's equivalent to (my $self)->{agent} = WWW::Mechanize->new ();

Achilles: Yup. It's compile time, not run time. Redeclares $self

Zeno: That code is strict-safe. Even though I wonder why my $self->{foo} doesn't raise an "Can't use undef as a reference" error

Zeno: I guess this could be construed as a bug, but I'm not sure where the bug starts. In theory, it should be a runtime bug, because as Achilles says, it should be equivalent to (my $self)->{agent}, which should be a (de)ref error.

Achilles: Interesting. In Komodo it shows as a warning in the editor so I assumed it was a stict issue

aspirant: Ahhh, I see now

aspirant: Thanks guys! That "my" should definitely not be there.

Zeno: Ah. $self then autovivifies? into a hashref. It shouldn't IMO.

Zeno: At least not while strict is enabled.

Zeno: The behaviour differs between perl -Mstrict -e "undef->{foo} = 'bar'" # fails and perl -MData::Dumper -Mstrict -e "my $self; $self->{foo} = 'bar';print Dumper $self" # doesn't fail

Achilles: Hmm, yes, it does look like a Perl bug.

Tortoise: Zeno my isn't a named unary operator; it has precedence above -> it seems

Zeno: I'm not sure if that can be explained through autovivification, because I think that the top-level thing should be a reference.

Tortoise: Zeno what? why would strict affect autoviv?

Zeno: Tortoise : I can take the my out of the equation (see above oneliners)

Achilles: Even so Tortoise $self should be undef

Zeno: Tortoise : For lack of a better term, I used "autovivification" for "turn undef into a hashref", which I think is what happens on deeper levels. It shouldn't happen at top-level, but maybe that's just consistent with what happens deeper down.

Achilles: Err, in other words - wot Zeno said

Achilles: Achilles heads off for coffee before he says anything silly(er)

Tortoise: Zeno afaict it should happen; your undef-> case fails because PL_sv_undef is readonly I think

Zeno: Tortoise : Yes. I think the behaviour of $self->{foo} = 'bar' is consistent with $self = {}; $self->{foo}->{bar} = 'baz' creating intermediate levels, but I'm not too sure whether strict should allow that at the toplevel. Whether it ...

Zeno: ... can prohibit that at the top level, I'm not sure. It would be hard I guess.

Tortoise: I think you are confusing things. autovivification at intermediate levels gets more attention because it's easier to do by accident, in a presumed "readonly" context. but it certainly happens at the outermost level too, in a modification context

Zeno: Tortoise : Ah

Zeno: I haven't witnessed it at the outermost level yet :)

Achilles: Damn pitty aspirant didn't post it as a SOPW. This is a discussion worthy of a FP'd node ;)

Zeno: Achilles: You could still make a meditation out of it :)

Achilles: Maybe one of you ought write it up as a Meditation?

and so, that's what I have done.

The bottom line is that Perl sees the second assignment as:

(my $self)->{agent} = WWW::Mechanize->new ();

which redeclares $self and sets it to undef. The ->{agent} = ... then autovivifies a hash ref to which the new $self is set. Perl never ceases to amaze!


True laziness is hard work

Replies are listed 'Best First'.
Re: You can autovivify that there?
by Anonymous Monk on Feb 23, 2009 at 10:23 UTC
    Congratulations, you've found a B::Deparse bug :)
    perl -MO=Deparse,-p -e" my $self = bless {}, 1337; my $self->{poop} = +6;" (my $self = bless({}, 1337)); ($my $self{'poop'} = 6); -e syntax OK
Re: You can autovivify that there?
by Bloodnok (Vicar) on Feb 23, 2009 at 11:52 UTC
    Excellent title and indeed, exposition.

    Still trying not to inject unnecessary humour I see - did you compose the title in one of your darker moments :D))

    ...refers GrandFather to a different sub-thread (if he hadn't already guessed:-)

    A user level that continues to overstate my experience :-))
Re: You can autovivify that there?
by ikegami (Patriarch) on Feb 23, 2009 at 15:38 UTC
    I wish autovivification of refs could be disallowed via a pragma. I wish it could even be a stricture, but there are backwards compatibility concerns.
      Maybe tye can extend his very useful Data::Diver module to be used as a pragma.
      P.S. $choice_of_title_name_there++

      print+qq(\L@{[ref\&@]}@{['@'x7^'!#2/"!4']});
Re: You can autovivify that there?
by zentara (Cardinal) on Feb 23, 2009 at 11:33 UTC
      dat derr