I just spent five minutes banging my head against a trivial mistake, and feel a need for confession.

I was planning to write a piece of code that would branch one of three ways, using the following construct:

my $state = ( 'b == a', 'b < a', 'b > a', )[$a <=> $b];

It's just an anonymous list that uses the output of the UFO operator as an index. It works because the UFO operator returns (-1,0,1), and Perl interprets -1 as the index of the last item in a list. The code has kind of a heavy Perl accent, but I like it because it's compact and easy to work with.

Thing is, I hadn't written that bit of code in a while, so I popped open a scratch file and wrote a toy version to refresh my memory. And that's when things blew up.

What I actually wrote was:

print ( 'b == a', 'b < a', 'b > a', )[$a <=> $b], "\n";

which gave me a syntax error, but the variant form:

print [ 'b == a', 'b < a', 'b > a', ]->[$a <=> $b], "\n";

worked just fine.

Like I said, I ran in circles for about five minutes before finally remembering that print() has a higher parentheses-precedent than anonymous lists do. The print statement was gobbling up the parentheses before they could be interpreted as an anonymous list.

I felt pretty dumb at that point, but when I do get hit by mistakes like that, I look for ways to avoid them in the future. In this case, there were two options. One was to stop being sloppy with my print() statements, and always include the parentheses. The correctly punctuated version:

print (( 'b == a', 'b < a', 'b > a', )[$a <=> $b], "\n");

does exactly what it's supposed to.

The other is to switch to the variant form above, which uses an anonymous listref instead of an anonymous list. Getting rid of the parentheses gets rid of the precedence issue entirely.

Of the two, I think I prefer the second. For one thing, I don't think I'll ever break myself of the print $x, "\n"; habit when I'm writing quick and dirty test code, but there's another, more respectable benefit. The most readable (to me) anonymous hash lookup takes more or less the same form:

my $status = { 'key 1' => 'value 1', 'key 2' => 'value 2', 'key 3' => 'value 3', }->{ $key };

so for the price of a couple extra characters, I get harmony between the list and hash versions. Plus, those versions are free from ambiguous-parentheses problems, which makes them both stronger and easier to read in the long run.

So.. for anyone tempted to use these kinds of lookups, I strongly recommend you pick up the []->[] and {}->{} forms right from the start. In the long run, they're less embarrassing.

Replies are listed 'Best First'.
Re: The perils of being tricky
by gaal (Parson) on Jun 04, 2005 at 18:34 UTC
    You don't have warnings turned on, do you?

    Here's a way not to have to put parentheses around everything. Use the unary + operator:

    print +( 'b == a', 'b < a', 'b > a', )[$a <=> $b], "\n";

      You don't have warnings turned on, do you?

      Not for the quick and dirty stuff. I use -w and strict at specific points in my development process, but usually I stick to techniques that I know to be clean.

      It's probably just a personal thing, but I find that constantly using warnings is worse for me than checking my work at intervals. If I get a warning during continuous use, I just correct it and move on. My primary goal at that point is to make sure my latest piece of code produces the result that I want. When I use warnings at intervals, each warning pass is a specific test of the code's integrity. My only concern at that point is whether the code is clean, so I take the time to find ways to make sure my code stays clean in the future.

      Mistakes like this one show me when I'm playing with things I shouldn't play with at all. If I need mechanical assitance to write a piece of code correctly, I probably won't be able to read it six weeks from now. And IMO, writing code that even I can't read without assistance is a problem. If I can't find a version that works, is clean, and is easy for me to remember, I write the technique off as being too tricky and look for other solutions.

        However, the +(..) "trick" is very well-known and comes up very often (for me at least). It's documented in perlop and is worth knowing.

        Cool trick, though!

        That's like saying you only write code you know to be bug-free :-)

        I know I make mistakes all the time. That's why I use as much unintrusive help as I can get. I'm not making an ideology of sloppiness, of course, but I don't chastise myself unduly for things that can't be helped. Oh, perl found a problem in my code? My bad; let's move on.

        (Of course I'm a better coder now than I used to be. I do learn from my mistakes; I just try not to spend overly much time on the learning itself. Also, warnings in this case and in many like it would have saved you from a real trap.)

      Note that the code cited by the OP doesn't give a warning; it dies with a syntax error (as the OP stated) first.

      Update: yikes, it does so give a warning; how did I miss it?!

Re: The perils of being tricky
by theorbtwo (Prior) on Jun 04, 2005 at 18:43 UTC

    There's a better solution here. Always use warnings, especially use warnings 'void';, which will give you a "useless use of a constant" warning, since your "\n" isn't doing anything...

    (OK, looking at the output, this is one of the few cases where you get a useful "'print (' interpreted as function" without getting some other error... but the advice still holds. Use warings, or -w, even on quick one-liner check code, esp when something strange is happening.)


    Warning: Unless otherwise stated, code is untested. Do not use without understanding. Code is posted in the hopes it is useful, but without warranty. All copyrights are relinquished into the public domain unless otherwise stated. I am not an angel. I am capable of error, and err on a fairly regular basis. If I made a mistake, please let me know (such as by replying to this node).

      Looks like you posted while I was writing the reply above. IMO, a syntax error is warning enough. If I can't fix the error without assistance, I'm probably in over my head and should look for a simpler way of doing things.

Re: The perils of being tricky
by perrin (Chancellor) on Jun 05, 2005 at 02:57 UTC
    Neat, but too tricky to actually use, in my opinion. It takes a few seconds just to parse it mentally and the order of the results is not very obvious. A more explicit piece of code would be worth the extra lines to me.

      I agree. If expect to use the code for more than once, I always take the time for readability. And I usually try to leave a comment line saying what I was thinking at the time, not how I wrote it that day - 'comment at the level of intent' as McConnell would say.

      For every Perl idiom, there is usually more than one way of writing it - TMTOWTDI, after all - and they will surely occupy different points on the readability spectrum. Not to mention the 'pedanticness' spectrum.