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

Based on the following simple code I expected the output to be something like:
0 foo
calling foo
1 foo
calling foo
..
10 foo
but what you get instead is:
0 foo
calling foo
and that's it, no more output, no error, so the main body of the eval was able to call foo, but for some reason, foo is unable to call itself, and I can't find anything anywhere that says that an eval string is not reentrant...So why can't foo call itself? I'm running perl 5.8.0 built for i386-linux-thread-multi. Does this have something to do with the fact that my version of perl is multithreaded? Many thanks for any wisdom you can empart on the subject.
$e = "my \$i=0; sub foo{ print \"\$i foo\\n\"; if (\$i < 10){ \$i++; print \"calling foo\\n\"; foo } } foo;"; eval "$e"; print $@


daN.

Replies are listed 'Best First'.
•Re: eval strings non reentrant?
by merlyn (Sage) on Jun 01, 2004 at 19:03 UTC
    Another problem that would have been solved with use strict. The problem is that the foo subroutine is not defined until the end of the definition, so your foo of the last line of the subroutine is compiled as a simple bareword text string constant instead of a recursive call. You get the initial call because your foo outside the subroutine call is in fact after the subroutine is defined.

    -- Randal L. Schwartz, Perl hacker
    Be sure to read my standard disclaimer if this is a reply.

      <hang head in shame>
      you're right of course adding () to the bareword foo made the problem go away, I though I had found the reason a bigger program that does much the same thing was failing it however report in $@ it however already has the () because two parameters are being passed to it, it sets $@ to
      Can't call method "gm" without a package or object reference at (eval 2) line 99.
      which leads me to believe now it's not the same problem at all..well that and there is a use strict in that one and it doesn't complain.


      daN.
Re: eval strings non reentrant?
by Joost (Canon) on Jun 01, 2004 at 19:04 UTC
Re: eval strings non reentrant?
by hv (Prior) on Jun 01, 2004 at 19:07 UTC

    The output I get disagrees with what you report - you did run this with warnings enabled, didn't you? The first piece of output I see is:

    Unquoted string "foo" may clash with future reserved word at (eval 1 +) line 8.
    .. which makes is clear (to me, at least) that the bare foo in the if block is being interpreted as an "unquoted string", not as a subroutine call. Putting parens after it makes it an unambiguous subroutine call, satisfies the warning, and gives your expected output.

    Hugo

Re: eval strings non reentrant?
by pg (Canon) on Jun 02, 2004 at 04:38 UTC

    I will do it this way:

    use strict; use warnings; my $e = "foo(0)"; eval $e; sub foo { my $i = shift; print "$i foo\n"; if ($i < 10){ $i++; print "calling foo\n"; foo($i); } }

    Other than the direct issue others already pointed out, there is two other style things:

    1. $i is not well scoped. You never define a variable with a scope even slightly bigger than its need. In case, $i shall be defined within the foo() function, and be passed as a parameter when it calls itself.
    2. It might seems okay for a demo, but define a sub in a string is not quite a good idea.

    Your guess that it has something to do with multithreading is... as multithreading will not help in this case.

Re: eval strings non reentrant?
by gmpassos (Priest) on Jun 02, 2004 at 05:30 UTC
    Perl syntax is complexer than we think, since simple things can have multiple means. A word, in your case "foo", declared as it was, can be:
    foo = sub foo {} foo = string "foo" foo = IO handler *foo foo = constant, that actually is: sub foo { "constant" }
    So, when the Perl syntax is parsed, first it will check if we have a sub with that name, if not, it will treat it as a string. As you can see, in your code, sub foo will return the string "foo".

    Next time, when calling a function, always use () or &:

    foo() # or &foo # that is the same of foo(@_) # or just to be explicit: &foo()
    Take a look in perlsub... And http://perldoc.com is your friend.

    Now about the messages of the "use strict" & "use warning" corporation, forget, since know that we need to use that doesn't means that we are goo programmers. There's a lot of things that we can't do with all the strict options enabled, and with all the warnings enabled. And Perl is here to let the developer to be free.

    But use at least "use strict qw(vars)" will save you a lot of time.

    Graciliano M. P.
    "Creativity is the expression of the liberty".

      guess this would work.. some small changes.. not too complex
      $e = "my \$i=0; sub foo{ print \"\$i foo\\n\"; if (\$i < 10){ \$i++; print \"calling foo\\n\"; &foo; } } &foo\;"; eval "$e"; print $@

      tc,
      Garry
        Note that &foo will send the same arguments received by the function, since &foo is the same to write foo(@_), and is not the same of foo()!

        Graciliano M. P.
        "Creativity is the expression of the liberty".