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

I've been refactoring the Parrot build tool pmc2c.pl for a month-and-a-half now, and only last night did I realize how mysterious I find the following formulation:

my $fh = open_file("<", $file, $verbose); my $class; eval do { local $/; <$fh> }; die $@ if $@; close $fh; return $class;

$file, when opened (I suspect) looks like this:

$class = { big => "hashref" };

When I surround this code with print statements for debugging, I find that $class is undefined when first declared but fully populated at the point where it is to be returned -- even though there is no explicit assignment to $class in this program.

Let me illustrate. Suppose that I have a file called alpha.txt that reads like this:

$class = { alpha => 'beta', gamma => 'delta', epsilon => 'zeta', };

In my main program, I then call:

use strict; use Data::Dumper; my $file = q{alpha.txt}; my $class; open my $fh, "<", $file or die "Unable to open: $!"; eval do { local $/; <$fh> }; die $@ if $@; close $fh or die "Unable to close: $!"; print Dumper $class;

I get this output:

$VAR1 = { 'gamma' => 'delta', 'epsilon' => 'zeta', 'alpha' => 'beta' };

Now, I would not have been surprised to get this result if my code had read:

use strict; use Data::Dumper; our $class; my $file = q{alpha.txt}; require $file; print Dumper $class;

... which produces exactly the same output. But in this case I know I have to use an our variable; a my variable will not suffice. So how come eval do {} enables me to get away with a my variable in the former case?

Thank you very much.

Jim Keenan

Replies are listed 'Best First'.
Re: eval do {} -- magical variable assignment?
by bart (Canon) on Dec 30, 2006 at 01:12 UTC
    The "do" has nothing to do with it. It's all magic of the eval $string construct.

    Try this, and you'll see the same effect:

    my $string = '$data = "foo"'; { my $data; eval $string; print $data; }

    In both perls I tested this in (5.6.1 and 5.8.3), this prints "foo".

    The do BLOCK only serves to locally undef $/, and thus, read the entire file into the string. The result of it, is just a string, that then gets evalled.

    You are right that it's a remarkable difference with require and use, that are indeed somewhat similar, as is do STRING; but that's a different kind of do, unrelated to this here, which is do BLOCK.

      It's all magic of the eval $string construct.

      ... which I didn't pick up on because I rarely use either eval $string or do.

      Thanks, monks!

      Jim Keenan
Re: eval do {} -- magical variable assignment?
by liverpole (Monsignor) on Dec 30, 2006 at 01:08 UTC
    Hi jkeenan1,

    I'm not so surprised by that result.  The eval documentation states:

    The value of the expression (which is itself determined within scalar context) is first parsed, and if there weren't any errors, executed in the lexical context of the current Perl program, so that any variable settings or subroutine and format definitions remain afterwards.

    where the operative words are "executed in the lexical context of the current Perl program".

    So there's nothing particularly magic there; it's just the behavior of good ol' eval.


    s''(q.S:$/9=(T1';s;(..)(..);$..=substr+crypt($1,$2),2,3;eg;print$..$/
Re: eval do {} -- magical variable assignment?
by shmem (Chancellor) on Dec 30, 2006 at 02:50 UTC
    There's a difference between do BLOCK and do FILE (and require).

    do BLOCK just returns the last statement calculated inside BLOCK, and eval happily connects the evaluated code to the current scope. In contrast, do FILE and require "bequeath an unrelated lexical scope" on the included code according to perlfaq8. So, changing your code slightly

    #!/usr/bin/perl use strict; use Data::Dumper; my $file = q{alpha.txt}; my $class; #my $fh; #eval do { # open $fh, "<", $file or die "Unable to open: $!"; # local $/; # <$fh> #}; #die $@ if $@; #close $fh or die "Unable to close: $!"; do $file; print Dumper $class;

    results in

    $VAR1 = undef;

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
      Yes wow. And this is why it's funky - because my brain is parsing something like this:

      eval -> skip ahead to see if there is a " or a { nearby. read eval as "string eval" or "block eval" accordingly.
      Additionally, I suspect my brain was parsing out the "do" entirely since in many situations it is used simply because a block by itself won't work (ie. in the do/while construct).

      Yet another little nuance to add to my mental Perl parser. Which makes me wonder if the common saying:

      Nothing can parse Perl except perl
      Shouldn't be re-phrased to something like:
      Nothing can parse Perl except perl or Larry Wall's brain
      :)