I just learned, the hard way, that || doesn't do what I mean in list context. I'm sure it's in the documentation that I read years ago. But I've forgotten.

I commonly use $scalar || $other_scalar. Nearly as common is scalar_func() || other_scalar_func(). What I just tried, and was surprised that it didn't work, was list_func() || other_list_func().

What I meant was "if list_func() returns an empty list, call other_list_func() and use its list, otherwise use list_func's list return." What perl thought I meant was something completely different.

#! perl -l use strict; use warnings; sub list0 { qw() } sub list1 { qw(foo bar); } sub list2 { qw(baz zap) } sub do_it { list1() || list2(); } sub do_it_right { my @x = list1(); @x = list2() unless @x; @x; } sub do_it2 { list0() || list2(); } print join ':', do_it(); print join ':', do_it_right(); print join ':', do_it2();
Output:
bar foo:bar baz:zap
My original code was much more complex - and I'm not even sure how, when it was like do_it, it actually came back with "1" instead of "bar". But it did.

Remember, folks: || gives you a scalar context on the left side of the operator. Just like it says in perlop under "C-style Logical Or". <sigh>

I wonder if perl6 will gives us a bit more syntactical sugar for this ;-)

PS: for those who haven't seen this English idiom, the title doesn't mean || has a bug. It means that I've grown addicted to using it. Sort of like "bitten by the gambling bug" means that one is gambling a lot.

Replies are listed 'Best First'.
Re: Bitten by the || bug
by TimToady (Parson) on Feb 16, 2006 at 23:57 UTC
    Well, it's enough of a bug that it's fixed in Perl 6:
    pugs> sub foo () { 1,2,3 }; say foo() || (4,5,6); 123 bool::true pugs> sub foo () { () }; say foo() || (4,5,6); 456 bool::true
Re: Bitten by the || bug
by samtregar (Abbot) on Feb 16, 2006 at 18:00 UTC
    I've learned to look closely at ||s when I find them in other people's code. Not for the problem you found, but for this nasty one:

    open my $fh, "<foo.txt" || die("This will never die!");

    The || binds too tight in this case, producing a very nasty silent skip on error checking.

    -sam

Re: Bitten by the || bug
by brian_d_foy (Abbot) on Feb 16, 2006 at 18:25 UTC

    The perlop documentation specifically says not to use || like that. :)

    --
    brian d foy <brian@stonehenge.com>
    Subscribe to The Perl Review
Re: Bitten by the || bug
by Aristotle (Chancellor) on Feb 20, 2006 at 02:40 UTC

    For hack value, here is an expressive version:

    sub also_correct { @{ ( map { @$_ ? $_ : [ list1() ] } [ list0() ] )[0] } }

    (For some reason @{} executes its expression in void scalar context, which necessitates the ()[0] gymnastics to force the correct context on map, which would otherwise return 1. That cost me some time to track down, because I was testing on the shell in one-liners with no strict. In retrospect that was stupid, considering I was testing code with references.)

    This is the list-context equivalent of list0() || list1() and has the same short-circuiting behaviour as ||.

    Makeshifts last the longest.

      s/void/scalar/, as should be expected since @{} wants a reference to an array, not a list of anything.

      - tye        

        So it is scalar context. (Thanks for the correction on wantarray.)

        By way of an explanation: I misremembered the issue I had in mind. What I was thinking of is that the scalar reference-taking operator \ evaluates its argument in list context. This came up in discussion of the (ab)use of @{[]} or ${\()} constructs to put code inside here-docs.

        Makeshifts last the longest.

        $ perl -le'print @{warn wantarray ? 1 : defined wantarray ? 0 : "undef +"}' undef at -e line 1.

        Nope, void context.

        Makeshifts last the longest.

Re: Bitten by the || bug
by EvanCarroll (Chaplain) on Feb 19, 2006 at 03:05 UTC
    Juxtapose with this slightly clean*er* method:
    sub list0 { qw() } sub list1 { qw(foo bar); } sub do_it_right { scalar list1() ? list1() : list0(); } $,=':'; print do_it_right();


    Evan Carroll
    www.EvanCarroll.com

      That is only cleaner if the default function (list1 in this case) is Memoize'able, and reasonably fast (or already memorised). For example, if list1() was really $iter->(), where &$iter was a sub reference that iterated over all possible combinations of some other list. Calling it twice has the side effect of missing every other entry. And you have about a 50% chance of having do_it_right() return an empty list or undef or whatever shows the termination of the iteration from list1() depending on whether the number of combinations is odd or even.

      Or, let's say list1() was long-running. e.g., looking data up on a web page, pulling data from a remote database server, finding the time it takes for 4 pings to a server in another country. You don't want to call it twice.

      So, yes, for the given sample data (which I thought was obvious that it was merely sample, and not actually representative of real code), you're right, that may work fine. But then you're missing the point of the meditation: the convenience of the || operator in scalar context (each item evaluated once and only once), and how I mistakenly thought that this applied in list context, even though, as brian_d_foy points out, perlop explicitly says not to do it.