Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw

OO-call bug uncovered & autovivified functions: defined? exists?

by perl-diddler (Chaplain)
on Oct 28, 2007 at 21:53 UTC ( [id://647749] : perlquestion . print w/replies, xml ) Need Help??

perl-diddler has asked for the wisdom of the Perl Monks concerning the following question:

I figured out the bug in perl that I mentioned a while back regarding OO-methods not being called when I expected them to. They also raise an interesting question about desirable behavior. The question about behavior first, followed by the code showing the bug(s) & probs.

What does it mean to "autovivify" a function -- i.e. how would one use such functionality (or is anyone using such?). If you believe autovivifying a function is a useful feature, then shouldn't you be able to use "exists" to see if the function actually exists yet (if the function has been initialized with a value). Currently, "exists()" only works on arrays and hashes. How would you code it?

If you allow exists to work with a function or method call, then shouldn't you also allow exists to work with autovivified scalars? It would mean the same thing as for array or hashes: returns true if the specified scalar has ever been initialized & the scalar (or function) is not autovivified if it doesn't exist.

Maybe the parser would be smart enough to figure things out, but it seems the construct "$a=exists &b", would normally be thought to invoke "b" -- not what is wanted. So would you need to encode the function name in a string, ala: "$a=exists '$b'"? What about scalars -- same problem? Or would the parser figure out scalars the same way it does now with array and hash refs ("exists $hash{$key}").

It might mean (if I understand), that "exists $b" tests if "$b" has ever been assigned a value. What'cha think? Here's the code demonstrating the problems (3 cases, 4 if you count the "exists" issue).


#!/usr/bin/perl -w #use strict; # can optionally enable to show no "unstrict" construc +ts ## ## this prog demonstrates 3 related problems and a 4th example ## that may also be a problem; (all 4, likely same cause) ## See descriptive text below after __END__ ## { package F; our $count=-1; sub boom { ++$count; $count==0 ? "two\n": $count==1 ?"one\n":"unprin +t\n";} } { package KA; our @ISA = qw(F); ## PROBLEM #1 'can' works even when 'boom' can't be called ## PROBLEM #3b ( -- call to boom fails 2nd time through) ## called as valid method sub innocent{ if ($_[0]->can("boom")) {$_[0]->boom; }} #ln 18 # sub laytrap { { my @dummy=(\&boom); #ln 22 # (see comments below about 4th "concern") # my $boom_defined=$dummy[0]; #ln 24 # print "boom defined = " . defined $boom_defined; #ln 25 + # print "; boom exists = " . exists $boom_defined; #ln 26 # print "\n"; #ln 27 } "zero:trap laid\n" } } package main; my $o = bless [],"KA"; ## These two calls (boom & innocent work... print $o->boom; print $o->innocent; ## lay trap to break boom & innocent print $o->laytrap; ## Now same two calls, boom & innocent, will fail: ## PROBLEM #2 -- can no longer call parent method 'boom' ## (using eval to catch error) eval {if ($o->can("boom")) {print $o->boom}}; #ln 43 $@ && print "$@"; ## PROBLEM #3 - within 'innocent', call to 'boom' fails ## this call terminates program (fatal Undefined error) print $o->innocent; __END__ The above prog demonstrates three (3) OO-call related problems. Perl autovivifies "\&boom" into "@dummy" in "sub laytrap" (ln 22, above). This *interferes* with OO calls through a blessed ref later on in program execution. The 4th "problem is the commented-out section above numbered lines "24-26" (in comments). _*If*_ you can autovivify functions or methods, then "exists()" should work with function references. Instead, if you uncomment lines 24-27, you will get an error: "exists argument is not a HASH or ARRAY element at ./pbug1 line 26." On the other hand, if you allow "exists" to work with coderefs, why not "simple vars"?

p.s. -- shouldn't "code" sections be displayed with a fixed font? Seems like my columns don't line up in the code above when I preview it...*sigh*

Replies are listed 'Best First'.
Re: OO-call bug uncovered & autovivified functions: defined? exists?
by ikegami (Patriarch) on Oct 29, 2007 at 01:30 UTC

    On p5p, where this is already being discussed, Rick Delaney presented a simpler snippet that demonstrates the problem.

    package F; sub foo { "foo" } package G; @ISA = qw(F); print \&foo; # creates entry in G's symbol table package main; my $o = bless [],"G"; print $o->foo;
    CODE(0x8149624) Undefined subroutine &G::foo called at - line 9.

    I'm not sure why perl-diddler didn't post this much more readable snippet...

Re: OO-call bug uncovered & autovivified functions: defined? exists?
by dave_the_m (Monsignor) on Oct 28, 2007 at 22:36 UTC
    I don't think it makes semantic sense for exists to be applied to functions. Exists applies to aggregates (hashes originally, later extended to arrays, where the semantics are slightly more dubious).

    What's the matter with defined &foo?

    Exists can't be applied to simple vars since there isn't sufficient state stored to be able to distingish between 'always been undef' and 'had a value, now undef'.


      I don't think it makes semantic sense for exists to be applied to functions.

      Nonetheless there are semantics for it.

      sub E; # exists, but not defined sub D { 1 }; # exists _and_ defined # sub N does _not_ exist $\="\n"; print exists(&N) ? "N exists" : "N doesn't exist"; print exists(&E) ? "E exists" : "E doesn't exist"; print exists(&D) ? "D exists" : "D doesn't exist"; print defined(&N) ? "N is defined" : "N is not defined"; print defined(&E) ? "E is defined" : "E is not defined"; print defined(&D) ? "D is defined" : "D is not defined"; __END__ N doesn't exist E exists D exists N is not defined E is not defined D is defined

      print "Just another Perl ${\(trickster and hacker)},"
      The Sidhekin proves Sidhe did it!

      Assuming of course, that &foo is equivalent to "simple var" in your sense. If you see globs as aggregates then defined &foo makes sense. And it's easy to see globs as aggregates, as you can do *foo{THING}. So defined (and exists) applied to global things would just translate to check the "slot in the glob" determined by sigil. Now, I don't know what really goes on down there in perl land, but from the outside this picture works for me.


Re: OO-call bug uncovered & autovivified functions: defined? exists?
by ysth (Canon) on Oct 29, 2007 at 02:25 UTC
    What do you mean by autovivify? Autovivification in the traditional sense (dereferencing something undefined and having the appropriate type of reference be automatically generated) doesn't seem to apply to code dereferences. But you seem to be talking about named subs, not code references at all?

    exists can indeed be used on subs; go back and reread the documentation. It takes the form exists &foo or exists &{foo}, where foo is the name of a sub (sans sigil) or a simple scalar variable containing a code reference or (2nd form only) an arbitrary expression yielding a code reference.

    Update: just read through your code example. IMO there are no bugs there; you are created an undefined sub KA::boom. Such a sub takes precedence over a parent's boom. This can be useful in the case of a class using AUTOLOAD:

    package Parent; sub boom { die "please don't call me" } package Child; sub boom; # exists but not defined, same as your \&boom but created at + compile time sub DESTROY {} sub AUTOLOAD { die "call me instead" } package main; bless({},"Child")->boom
Re: OO-call bug uncovered & autovivified functions: defined? exists?
by Somni (Friar) on Oct 29, 2007 at 00:29 UTC
    You figured it out? Odd, I seem to remember spending a chunk of time cutting down your code in order to determine what the problem was, then providing you with some simple examples. Thanks for the credit.

    It's possible taking a reference to a function shouldn't behave this way. Unfortunately, in practice, with the way Perl is implemented, it needs to. In order to be able to take a reference to a function that has not yet been defined you have to add a symbol table entry. ->can, defined, etc. all need to work with this symbol table entry, because you've essentially made a promise that you will define the subroutine.

    If you remove this behavior then it will be required that all subroutines be defined before ever being referred to. Given the dynamic nature of Perl you can't even check this promise until the subroutine is called; a subroutine could be defined at any point during compile- or run-time.

      If you remove this behavior then it will be required that all subroutines be defined before ever being referred to.

      Could you please substantiate this? I don't see why the autovivifying behaviour of \&foo is required to call functions not yet defined. In fact, the doesn't seem to be any such need currently.

      >perl -e"print exists(&foo)?1:0; exit; foo();" 0

      It's perfectly fine behaviour for sub foo; to create a stub, but that's an unrelated matter.

        Consider, you take a reference to \&foo in one part of your code. No symbol table entry is created, but somehow taking the reference Just Works. Later in the same code another reference is taken, \&foo. If no symbol table entry is created, how can these two references refer to the same subroutine?

        There has to be some agreement on what underlying subroutine these two references refer to. This has to agree with the subroutine body that, supposedly, is eventually created.

      Um..sorry, but who are you and where did you give me help (i.e. in this forum or elsewhere?)

      I had the example pared down to a simple test case, which, when one ran it, printed out what was going on. No one seemed to like that -- they just wanted the raw code without the clear output.

      I posted that to p5p, and someone posted about a 10-line case that demonstrated 1 example of the same bug. People grokked that example and easily understood it. So I built-up from that example & added back in the other test cases to make sure I had bracketed my problem -- no, it didn't happen "here", yes, it happens here. Here is code that points the the problem in the perl internals...seems that the undefined value overrides the parent-method search...etc.

      If you were the person who posted the code to p5p, it showed me what type of example people wanted, thanks, but the code posted showed the problem (but not as succinctly) in multiple ways to provide more information about what was going on, like how the 'can' function printed out coderef to two functions before the bogus reference, but after, the unreferenced function still had same "can" value (same CODEREF(hexnum)) while the referenced function had a different "can" value. This seemed to show that perl created a new value to store & reference as the "coderef" -- but one that was also incorrect.

      I'm sorry if you feel I didn't give you enough credit for something you remember doing. If you provided help, thank you. I do appreciate your attempts to do so, but just because you may have given me help doesn't mean I "got" the help and that's when a light turned on in my head. The last person to help me from this forum was telling me to print out the reference, before the access error. This frustrated me, as I had a special routine written just to display the value -- it checked if it was a valid reference, if so, it printed the "ref $obj-pointer" value, then tested the pointer against all (2) classes (using "isa") and printed out membership information. Then it looped through the list of methods with "can" to verify that perl still thought I "could" do all of the methods through that reference. I.e. I printed out its value and a bunch of information about it -- stuff that was already in the program before anyone suggested I print the value.

      Thanks again for any help you provided, I didn't mean to slight anyone, but until I had the problem encoded and posted to p5p, it wasn't too clear to me that anyone even believed it was a real bug in perl.


        Apparently you don't remember your own thread here, and the eventual solution. Perhaps you stopped reading. The end of the thread is here.
Re: OO-call bug uncovered & autovivified functions: defined? exists?
by ysth (Canon) on Oct 31, 2007 at 22:30 UTC
    You've gotten a lot of flack (mostly elsewhere) about this, but I wanted to take a moment to thank you for taking the time to report a suspected bug and present your arguments; even if you turn out to be on the wrong track, poking into things that don't work as you expect and reporting the results can be immensely valuable. I really hope you don't let all the friction keep you from doing the same thing next time.