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

An attempt of doing explicit lazy evaluation in perl 5
use strict; use warnings; my $i = 1; #return a pointer to a function that should be invoked sub makeLazy { my ($inp) = @_; sub { eval $inp; }; } my $part2 = makeLazy('print "\nnow its $i";'); print "\ni is ".$i++; $part2->(); print "\ni is $i";
Testing this code by
% test1.pl i is 1 now its 2 i is 2
Hurray! It *seems* to work alright. But what happens when i move the declaration of $i?
use strict; use warnings; #return a pointer to a function that should be invoked sub makeLazy { my ($inp) = @_; sub { eval $inp; }; } my $i = 1; my $part2 = makeLazy('print "\nnow its $i";'); print "\ni is ".$i++; $part2->(); print "\ni is $i";
Lets give it another testrun:
% test1.pl i is 1 i is 2
Eeks! Nothing happens, the $part2->() does nothing and return nothing. It doesnt even eval $i to nothing, it just does not execute! What is the cause? What is a workaround?

Replies are listed 'Best First'.
Re: Not-so-lazy evaluation?
by Joost (Canon) on Nov 06, 2007 at 15:50 UTC
    $i is not declared when your string is eval()d and you're not testing if the eval succeeds:

    use strict; use warnings; #return a pointer to a function that should be invoked sub makeLazy { my ($inp) = @_; sub { eval $inp; die if $@; }; } my $i = 1; my $part2 = makeLazy('print "\nnow its $i";'); print "\ni is ".$i++; $part2->(); print "\ni is $i";
    Global symbol "$i" requires explicit package name at (eval 1) line 1. ...propagated at test.pl line 8.
    Why are you not just doing
    use strict; use warnings; my $i = 1; my $part2 = sub {print "\nnow its $i"; }; print "\ni is ".$i++; $part2->(); print "\ni is $i";
      > $i is not declared when your string is eval()d
      That baffles me. At compile time, perl is just having a string. It is at run time, when the execution reaches the $part2->(); statement that the string is evaled inplace and the string is compiled and evaled. At that point in time, $i should be declared (initial post , perl codelisting 2) in my newbie opinion.

      Basically that opinion shows faulty but questions still remains. When is $i evaled? Or in what dictionary/environment/whatever-mechanism-is-used?

      The value of $i that is printed (in the first perl program) is the value that i was expecting IF it was evaled at the line $part2->(); It looks to me like there is a mismatch between the places where it is defined and where it is executed. I am assuming this to be the same place. If it is at the place of definition of makeLazyFunction, then i expect $i to have the value 1 and not 2. If it is at the place of $part2->(), then i expect $i to have the value 2 and to be defined as well. Are both views erroneous? What is the correct view?

      > Why are you not just doing
      Because of the same problem i run into with my code (but i didnt knew until now that it was the same problem): if you defer the declaration of $i then you get Global symbol "$i" requires explicit package name at test1.pl line 7. Only with my code it doesnt show because the error is only generated at run-time and is not printed. No printing, no error i was thinking, alas mistakenly.
        > $i is not declared when your string is eval()d That baffles me. At compile time, perl is just having a string. It is at run time, when the execution reaches the $part2->(); statement that the string is evaled inplace and the string is compiled and evaled. At that point in time, $i should be declared (initial post , perl codelisting 2) in my newbie opinion.
        Lexical variables (such as my $i), are, um, lexically scoped.

        When the eval tries to compile the code and sees a $i, it scans out through the lexical scopes looking for a matching $i declaration. This scanning starts at the physical location of the eval itself within the text. ie it looks for my $i in the text of the eval string (somewhere before the $i), then in the text of the anon sub (somewhere before the eval), then in the text of the makeLazy sub (somewhere before the anon sub definition), then in the main body of the file itself (somewhere before the makeLazy definition).

        Since it doesn't find a lexical $i delaration in any of those scopes, it assumes you meant the package variable $i, which then gives an error under strictures.

        Dave.

        Basically that opinion shows faulty but questions still remains. When is $i evaled? Or in what dictionary/environment/whatever-mechanism-is-used?

        In your case, $i is evaluated when the string containing a literal '$i' is eval()ed. You could evaluate $i earlier by interpolating $i into the string (evaluating $i when the string is created), but then, that would convert the value of $i into a string, which isn't always what you want:

        makeLazy(qq(print "\nnow its $i"));
        Note that this also decouples the eval'd code in makeLazy from the actual current value of $i.

        Because of the same problem i run into with my code (but i didnt knew until now that it was the same problem): if you defer the declaration of $i then you get Global symbol "$i" requires explicit package name
        Well, what do you want it to do? Either you want a lexical variable, but then you must declared it in its lexical scope, or you may want a (possibly local()ized package variable) - which you don't have to declare, if you prepend them with the package name (or you *could* switch of strict 'vars').

        update: Note that in my alternative version, using my $defer = sub  { something_with($i) } syntax, $i is evaluated when $defer->() is called, but the scoping of $i is resolved at compile time, and figuring out the actual instance of $i may be resolved when the $defer = .. assignment is executed (that last part is important if you're generating these $defer closures from within another subroutine, with $i declared lexical in the outer subroutine).

        You may find this column useful

Re: Not-so-lazy evaluation?
by dragonchild (Archbishop) on Nov 06, 2007 at 15:51 UTC
    If you're looking for a working version of lazy evaluation, take a gander at Scalar::Defer.

    My criteria for good software:
    1. Does it work?
    2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
      dragonchild >> google
      many thanks
        The more CPAN authors you hang out with, the more you know. Most of my PM answers have started to follow the form "See module XYZ::ABCD for a proper implementation of what you're trying to do." where XYZ::ABCD seems to be List::MoreUtils more often than not.

        My criteria for good software:
        1. Does it work?
        2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
Re: Not-so-lazy evaluation?
by johngg (Canon) on Nov 06, 2007 at 15:51 UTC
    It looks like it is a scoping issue. Add the line print $@ if $@; after the eval to show if it eval'ed correctly and you will see that the eval does not know about $i once you move it's declaration to after sub makeLazy.

    I hope this is helpful.

    Cheers,

    JohnGG