Limbic~Region has asked for the wisdom of the Perl Monks concerning the following question:

All,

Background: What got me thinking

As has been discussed recently:
my $persist if 0; # illegal >= 5.10.0
Is an unusual construct which effectively creates a state variable due to the weird way perl handles lexicals (compile and runtime). It has been argued, that my should never be used with a postfix conditional. That is probably a good idea even when you only want a lexical to be created if some other condition is true. An if block should be used for that.

Actual Question: (see this also)

My question has to do with the following unrelated construct:

if (my $result = some_func()) { # do something with $result }
There are a couple of reasons you might want to do this:

This leaves a few questions for me.

To be honest, I really don't care as I can count the number of times I have used that construct on one hand. I ask because the recent state discussions have made me wonder if Perl 6 is going to clean up all the cobwebs in the corners.

Questions Answered

Update: Feel free to continue to contribute if you have something to add, but my questions have been answered - see here, here, and here

Update: Added a couple of headings

Cheers - L~R

Replies are listed 'Best First'.
Re: What about if (my $var = foo()) { ... }
by friedo (Prior) on Dec 29, 2007 at 15:52 UTC
    if (my $result = some_func()) { # do something with $result }

    I use this construct all the time, and am not aware of any problems with it. The conditional is testing the return value of the assignment, which is true if some_func() returns something true. If it's false, then $result gets whatever false value was returned and the block does not execute (or the compiler may optimize away the assignment completely -- I don't know how that works.)

    Off hand, I can't think of any reason why this would not also be allowed in Perl 6.

      friedo,
      Off hand, I can't think of any reason why this would not also be allowed in Perl 6.

      In perl 5, we write:

      for my $name (keys %people) { # do something with $name }

      The $name lexical is implicitly scoped to the for block. This is not the case in perl 6. The way perl 6 solves this is by introducing parameters to the block so you achieve the same thing in a cleaner way (from a design perspective).

      Cheers - L~R

        Eh? Going away? What's the corresponding Perl 6 construct and how/why is it considered to be "cleaner"?
Re: What about if (my $var = foo()) { ... }
by duelafn (Parson) on Dec 29, 2007 at 16:27 UTC

    From Synopsis 4:

    if testa() -> $a { say $a } elsif testb() -> $b { say $b } else -> $b { say $b }

    Good Day,
        Dean

Re: What about if (my $var = foo()) { ... }
by grinder (Bishop) on Dec 29, 2007 at 16:06 UTC

    I think you have eaten too much Christmas pudding :) You seem to be thinking the following constructs are equivalent:

    • my $foo = 0
    • my $foo if 0

    These constructs are hardly equivalent and there is no ambiguity that needs to be cleaned up, regardless of whether they happen to appear within a conditional.

    A lexical declared within an if is not prone to the my $foo if 0 persistency problem, period. Consider the construct

    if (my $result = some_func())

    assuming some_func() is some sort of cyclic iterator that returns a positive number, then 0, then undef, the if branch will be taken only when $result contains a positive number. If $result contains 0 or undef, the conditional will evaluate to false, and the else branch (if any) will be taken instead.

    At no point will $result retain its previous contents. Even if it did at a purely implementational level, you're initialising it with a value via the assignment of the results of some_funct(), and so you're safe.

    • another intruder with the mooring in the heart of the Perl

      grinder,
      No, I assure you I know they are different and completely understand why postfix is wrong. Please read my response to dragonchild. My questions are specific to this construct.

      The reason we don't do:

      if (some_func()) { my $val = some_func(); }
      Is because some_func may change return values in subsequent invocations. The reason why we don't do:
      my $val = some_func(); if ($val) { # ... }
      Is because we want $val scoped as small as possible.

      This only works because of the implicit scoping in perl 5 which is going away in perl 6 (at least in for blocks). I am wondering if there is an alternative way of writing this that accomplishes both things.

      Cheers - L~R

        if you want to avoid:
        if (my $val = some_func()) { # ... }
        and keep as small a scope as possible, how about:
        { my $val = some_func(); if ($val) { # ... } }


        John
Re: What about if (my $var = foo()) { ... } (explicit)
by tye (Sage) on Dec 29, 2007 at 16:22 UTC

    See Re^2: Duh. 'my' scope in if else blocks.. If you still want that, then you just put an explicit scoping block around the area where the variable is used, of course. I'd probably put the assignment outside of the conditional as well.

    - tye        

Re: What about if (my $var = foo()) { ... }
by dragonchild (Archbishop) on Dec 29, 2007 at 15:52 UTC
    Those are different constructs. As has been discussed before, postfix notation only looks like the same as the normal versions. There are scoping and other differences between normal and postfix if, normal and postfix while, and normal and postfix for.

    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,
      Yes, I know they are different constructs (I said as much). Was giving background information into why I was asking the question confusing? If so, I am only interested in the construct in the thread title and the questions I asked about it. I know I must sound like a broken record by now, I have a newborn and a 2 year old at home and I haven't had much sleep lately.

      Cheers - L~R

Re: What about if (my $var = foo()) { ... }
by ikegami (Patriarch) on Dec 29, 2007 at 22:52 UTC

    I'm confused. if BLOCK already creates a scope (in 5.8, at least)

    >perl -c -Mstrict -we"if (my $r = f()) { g() } print($r);" Global symbol "$r" requires explicit package name at -e line 1. -e had compilation errors.

    Note that the scope is around the entire statement, so any elsif clauses, "then" blocks, elsif blocks and else blocks will have access to the variable.

    >perl -c -Mstrict -we"if (my $r = f1()) { g1() } elsif (my $r = f2()) +{ g2() }" "my" variable $result masks earlier declaration in same scope at -e li +ne 1. -e syntax OK

    Update: Nevermind, I understand the question now. Like someone's already said, there's nothing stopping you from creating an explicit block.

    >perl -c -Mstrict -we"{ my $r = f(); if ($r) { g() } } print($r);" Global symbol "$r" requires explicit package name at -e line 1. -e had compilation errors.

    Caveat: last, next and redo will ignore the implicit block but will treat the explicit bare block as a loop.

    >perl -le"sub f { 1 } for (1..2) { if (my $r = f()) { next } print ' +!' }" >perl -le"sub f { 1 } for (1..2) { { my $r = f(); if ($r) { next } } + print '!' }" ! !
Re: What about if (my $var = foo()) { ... }
by polettix (Vicar) on Dec 30, 2007 at 14:42 UTC
    This issue was discussed -- "inside-out" -- some time ago, starting from this post from merlyn and more or less ending with this reply from TheDamian.

    I would also add that the idiom could be useful even when the function is pure, but heavy on CPU. Just as a form of self-baked cache.

    Hey! Up to Dec 16, 2007 I was named frodo72, take note of the change! Flavio
    perl -ple'$_=reverse' <<<ti.xittelop@oivalf

    Io ho capito... ma tu che hai detto?
      polettix,
      Thanks! I tried hard to find this thread when I posted this because I remembered it. Unfortunately, I misremembered TheDamian being the one providing the solution and thought he was the one asking the question. Using Super Search to find posts by TheDamian asking a question (not a reply) didn't help and so I gave up.

      Cheers - L~R

Re: What about if (my $var = foo()) { ... }
by ambrus (Abbot) on Jan 26, 2008 at 17:13 UTC

    I've just found some code on my scratchpad which shows that apart from postfix modifiers, there's at least one other way where scoping rules get crazy. The following code behaves differently on perl 5.10.0 from 5.8.8.

    perl -we '@a = qw"a b"; while (my $x = pop @a) { push @x, \$x; print \ +$x, ": ", ($x || 0), "\n"; redo if 0 != @x % 3; }; '

    Note: I don't understand what happens in either case. I don't even really understand the simple my $x if 0 case.

      For the quote challenged
      perl -we " @a = qw!a b!;while (my $x = pop @a) { push @x, \$x; print \ +$x, q!: !, ($x || 0), qq!\n!; redo if 0 != @x % 3; };
      I believe the old behaviour is a bug, because $x isn't supposed to change