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

It was my understanding that Deparse gives me a parser-eye view of the code; that it turns what I write into a canonical form that parses identically. So if I have a script, try.pl, I can do
perl -MO=Deparse try.pl > try2.pl perl try2.pl
and get exactly the same result as I would get from just
perl try.pl
Having found a nifty way to get around the problem of being unable to modify a lexical in the same statement as it is declared, I wanted to see how the parser saw it. The original script:
#!perl use strict; use warnings; use Data::Dumper; s/foo/bar/ for (my $d = 'Shakespear is the food'); print "$d\n";
and its output:
Shakespear is the bard
The script Deparse generated:
use Data::Dumper; BEGIN {${^WARNING_BITS} = "UUUUUUUUUUUU"} use strict 'refs'; foreach $_ (my $d = 'Shakespear is the food') { s/foo/bar/; } print "$d\n";
and its output:
Name "main::d" used only once: possible typo at try2.pl line 7. Use of uninitialized value in concatenation (.) or string at try2.pl l +ine 7.
What's broken? Deparse, or my understanding of it?

Update: Ok, so what I thought was nifty isn't so nifty. You can't use a lexical by name, but you can modify it easily enough with the usual

(my $d = 'Shakespear is the food') =~ s/foo/bar/;
Still, the predicate-for trick would give you a variable you could manipulate for something like
/foo/ and $_ .= '1' for (my $d = 'Shakespear is the foo');

Caution: Contents may have been coded under pressure.

Replies are listed 'Best First'.
Re: Deparse broken or just misunderstood?
by davido (Cardinal) on Nov 15, 2004 at 16:35 UTC

    You get a feather in your obfuscated code contest cap when you are able to write code that doesn't work when deparsed. Yes, it's possible to foil deparse. Why? Only perl itself can parse Perl.

    In the case of your example, how would you want deparse to deparse that construct? Something more like this?:

    my $d = 'Shakespeare is the food'; foreach ( $d ) { s/food/bard/; }

    The problem is that of lexical scoping, and that of deparse trying its damndest to refrain from using 'for' as a modifier. I would say that at best your nifty trick is just a trick worthy of use in obfu, but not in mainstream code. I believe that the behavior allowing it to work in the case of the 'for' modifier is not defined, and thus may change in future Perl refinements. It's certanly not discussed directly in perlsyn, though perlsyn does mention that using my() on the left hand side of a conditional modifier yields undefined behavior.


    Dave

      Alternatively, just the declaration could be moved out of the for:
      my $d; foreach ($d = 'Shakespear is the food') { s/foo/bar/; }
      I think that would be my preference, although your suggestion (modulo typos) is perfectly fine.
      I believe that the behavior allowing it to work in the case of the 'for' modifier is not defined, and thus may change in future Perl refinements.
      Certainly the scoping of the variable is defined, and according to perlsyn, "a declaration can be put anywhere a statement can," and "apart from declaring a variable name, the declaration acts like an ordinary statement, and is elaborated within the sequence of statements as if it were an ordinary statement. That means it actually has both compile-time and run-time effects."

      I think the behavior is pretty well-defined by that. The only issue is the scoping difference between predicate-for and block-for, which Deparse didn't accout for.

      By the way, I agree that this trick is not for mainstream code. Nifty tricks are not elegant solutions, but they can be useful for getting a better understanding of what lies behind a limitation.
      And it's less ugly than

      (sub { $_[0] =~ s/foo/bar/ })->(my $d = 'Shakespear is the food');
      (which Deparse doesn't change materially) :-)

      Caution: Contents may have been coded under pressure.
Re: Deparse broken or just misunderstood?
by Zaxo (Archbishop) on Nov 15, 2004 at 16:38 UTC

    That sure looks like a bug - the two are inequivalent. The Deparsed version introduces a narrower scope for the lexical $d than is in the original.

    After Compline,
    Zaxo

Re: Deparse broken or just misunderstood?
by BrowserUk (Patriarch) on Nov 15, 2004 at 17:08 UTC

    The problem seems vaguely related to another for loop behaviour that I think is unintuative and could be construed as a bug.

    Sometimes you need to know what value a loop counter had when the loop exited early. If you use my on the loop counter, then it has gone out of scope when the loop exits and this information is lost.

    The apparently obvious thing to do is to declare the loop counter before the loop so that it still exists afterwards, but this doesn't work:

    #! perl -slw use strict; my $i = 123; for $i ( 0 .. 10 ) { last if $i == 5; } print $i; __END__ 123

    Even though the strict is enabled, no warning is issued, which means the $i used by the for loop can only be the lexical declared above it. But still the value of $i after the loop is the value set preceding it. The only way to view this is that $i has been localised for the duration of the loop; but you (the programmer) cannot use local on a lexical.

    It is probably a side-effect of making $i an alias for to the values it takes on for the duration of the loop, but it is unintuative and darned annoying.


    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "Think for yourself!" - Abigail        "Time is a poor substitute for thought"--theorbtwo
    "Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side." - tachyon
      That behavior is, at least, fully documented. for loop variables are always localized (or lexically declared) to the block. Predicate-for cannot have a named loop variable. (Note: my situation did not concern itself with loop variables, but lexicals declared in the list of a block-for are scoped to the block, while those in a predicate-for are scoped to the enclosing block/file)

      It might be nice to expand the syntax to allow a reference to a scalar variable to be given as the loop variable, and the referenced variable would be used instead of a newly-created variable.

      my $i = 123; for \$i ( 0 .. 10 ) { last if $i == 5; } print $i; __END__ 5
      The present situation provides a use for a C-style for loop:
      my $i = 123; for ($i = 0; $i <= 10; ++$i) { last if $i == 5; } print $i; __END__ 5

      Caution: Contents may have been coded under pressure.