http://qs321.pair.com?node_id=728150

What's the most frequent type of bug you write when working in perl? These days, mine seems to be list context bugs. I try to do this:
ShoppingCart->new( user => $user, map { $_->id => $_->quantity } @products, );
But that really requires this:
ShoppingCart->new( user => $user, ( map { $_->id => $_->quantity } @products ), );
Contrived example, but hopefully you get the gist -- unexpected list context or lack of same is definitely a thorn in my side.

Anyone else have a class of errors that keeps biting them? Has it changed for you over your development as a Perl coder?

UPDATE

This was intended to be about the general question, not the example, but since people are complaining about the example, I'll offer one that works. CGI.pm will return undef or empty list for params with no value, depending on context. That means this code fails:

use CGI; use Data::Dumper; my $q = CGI->new(foo => 1); print Dumper [ foo => $q->param('foo'), bar => 1, ];
There are various ways to solve it, like forcing scalar context:
foo => scalar $q->param('foo'),
BTW, the thing I was thinking of with map was not context but the way that a function which takes a list can grab more than you meant it to of what follows:
ShoppingCart->new( user => $user, map { $_->id => $_->quantity } @products, bank_account => 99, );
Putting parentheses around the map solves the problem.

Replies are listed 'Best First'.
Re: your Perl bug Achilles heel
by TGI (Parson) on Dec 05, 2008 at 04:10 UTC

    Lately, I keep typing retrun. I guess I've watched The Shining too many times.

    For a while, I kept writing my %foo = { blah => 1 }; despite knowing perfectly well that I should be using parentheses. This was noxious because some fonts make it hard to distinguish {} vs <().

    Most of my problems seem to be some kind of touch-typing Tourrette's. The bugs change over time, but they seem to consistently be of that type.


    TGI says moo

Re: your Perl bug Achilles heel
by GrandFather (Saint) on Dec 05, 2008 at 01:06 UTC
    use strict; use warnings; my @products = ( {id => 'jam', quantity => 10}, {id => 'cream', quantity => 4}, ); mySub (user => 'Flibble', map {$_->{id} => $_->{quantity}} @products +); print "\n"; mySub (user => 'Flibble', (map {$_->{id} => $_->{quantity}} @products) +); sub mySub { my %params = @_; print "$_: $params{$_}\n" for keys %params; }

    Prints:

    cream: 4 user: Flibble jam: 10 cream: 4 user: Flibble jam: 10

    and the difference is?


    Perl's payment curve coincides with its learning curve.
      It's just a poor example. In my real code I was getting the wrong result from a map in a similar situation because the context was not what I expected.
Re: your Perl bug Achilles heel
by mirod (Canon) on Dec 05, 2008 at 10:58 UTC

    For me the main recurring problem is dealing with encodings. I use a few tools that stick to old-fashioned ISO-8859-1, but do most of my processing in utf-8. So I am already in trouble. Then the "silently downgrade anything that looks like it could be ISO-8859-1" policy that perl had to adopt for backward compatibility purposes adds to that. Especially since perl 5.10 took out the -CSD option. It makes it annoying to write quick one-off filters that work with STDIN/STDOUT.

    BTW I agree that the map problem is annoying, especially as it looks inelegant. You end up with parentheses, but no comma between the "arguments". It just doesn't look right. I have learned to get over it though, after years of writing lines like: print "foo ", join( ', ', map( { "$_: $h{$_}" } sort keys %h)), "\n";.

      perl -Mopen=:utf8,:std    ...?

        If you mean the other wau around (perl -Mopen=:std,:utf8 -E''), indeed that works. Thanks

        Overall it's still a pain though. At least for my brain! One-liners and scripts that don't work the same (you CAN use -CSD from the command line) mess me up.

Re: your Perl bug Achilles heel
by wfsp (Abbot) on Dec 05, 2008 at 07:57 UTC
    Mine is similar to the op. Telling built ins that take a list where the list ends.
    my %broken = ( monday => join q{/}, q{one}, q{two}, tuesday => sprintf q{%s%s}, q{three}, q{four}, ); my %better = ( monday => join(q{/}, q{one}, q{two}), tuesday => sprintf(q{%s%s}, q{three}, q{four}), );
    Rather than have to remember that parens are needed in this case I tend to use them all the time. If the function's args are on more than one line it gives you something to pin the ; to.
    my $str = sprintf( q{%s -> %s}, get_data(), get_more(), );
Re: your Perl bug Achilles heel
by rhesa (Vicar) on Dec 06, 2008 at 03:06 UTC
    The CGI thing has bitten me often enough that I've learned to avoid it, but I still have the teeth marks.

    What I also tend to get wrong sometimes is the difference in precedence between or and ||, especially in return statements. Usually when I pretend to be terse and write:

    sub is_foo_or_bar { return foo($x) or bar($x) } # no! use || here
    I don't do this often, but when I do, it can take a long time to debug, because that code looks deceptively sensible.
Re: your Perl bug Achilles heel
by Lawliet (Curate) on Dec 05, 2008 at 02:32 UTC

    Hmm, have not thought about this question before. I will have to update this node later because I will surely think of a better mistake; mixing up < and > when using open(). I know which one is read and which is write but I usually mistype them. It gets annoying.

    I'm so adjective, I verb nouns!

    chomp; # nom nom nom

Re: your Perl bug Achilles heel (parens)
by tye (Sage) on Dec 05, 2008 at 03:48 UTC

    Your example is not only contrived, it doesn't make sense. :)

    Adding parens as you did does not change any scalar context to list context.

    But perhaps that is part of why you are currently making mistakes regarding context (or that you think are about context)?

    - tye        

Re: your Perl bug Achilles heel
by Narveson (Chaplain) on Dec 05, 2008 at 23:56 UTC

    Lately my downfall has been buggy test scripts.

    • Wrong test count. I know there's a no_plan option, but that would leave me defenseless against
    • Premature death of test script. Often the result of trying to exercise exceptions and error handlers. Yes, I know, the remedy is eval.
    • Remembering how to use TODO and skip and skip_todo, or is that todo_skip? Finding out that skip if doesn't always protect a test script from tests that blow up, even though they're supposed to be skipped. See previous point.
    • Testing exec.
    • Forgetting to use diag for output, and printing something in a format that confuses the test harness.
    • Testing subroutines and methods that print their own output.

    It's still worth writing tests. Once I get a test to work, I can keep it around forever, and regression testing has saved me a lot of time over the years. I just wish there were a Test::Test module.