After several years of teaching Perl and helping other people solve their Perl problems, I wrote a guide that showed how I think through a problem. It’s appeared on a couple of web sites and there are even a several translations.
Some of the stuff I did unconsciously, and those are the hardest things to pass on to a new programmer. Now that I have this guide, other people can develop their own problem-solving skills. It might not solve all of your Perl problems, but it’s a good place to start.
I believe in three things when it comes to programming, or even everything else I do. Starting with the right attitude can help you avoid various subconscious blocks that prevent you from seeing problems.
Forget about code ownership. You may think yourself an artist, but even the old Masters produced a lot of crap. Everybody’s code is crap, which means my code is crap and your code is crap. Learn to love that. When you have a problem, your first thought should be “Something is wrong with my crappy code”. That means you do not get to blame perl. It is not personal.
Forget about how you do things. If your way worked, you would not be reading this. That is not a bad thing; it’s just time to evolve. We’ve all been there.
If you have a problem with your program it is just that–your problem. You should do as much to solve it by yourself as you can. Remember, everyone else has their own programs, which means they have their own problems. Do your homework and give it your best shot before you bother someone else with your problems. If you honestly try everything in this guide and still cannot solve the problem, you have given it your best shot and it is time to bother someone else.
Fix things so you do not have the same problem again. The problem is probably how you code, not what you code. Change the way you do things to make your life easier. Do not make Perl adapt to you, because it won’t. Adapt to Perl. It is just a language, not a way of life.
If you aren’t already using strictures, turn it on. Perl gurus are gurus because they use strict
, which leaves them more time to solve other problems, learn new things, and upload working modules to CPAN. When I program without strict
, I find that the first mistakes I make would have been caught at compile time.
You can turn on strictures within the code with the strict
pragma:
use strict;
You can turn on strictures from the command line with perl‘s -M
switch:
perl -Mstrict program.pl
If you use v5.12 or later and require that version of later, you automatically turn on strict
:
use v5.12;
You may be annoyed at strictures, but after a couple of weeks of programming with them turned on, you’ll write better code, spend less time chasing simple errors, and probably won’t need this guide.
Perl can warn you about a lot of questionable constructs. Turn on warnings
and help Perl help you.
You can use perl‘s -w
switch in the shebang line:
#!/usr/bin/perl -w
You can turn on warnings from the command line:
% perl -w program
Lexical warnings have all sorts of interesting features. See the warnings
pragma documentation for the details for the more advanced uses. I make it simple by turning everything on in the current scope:
use warnings;
If you don’t understand a warning, you can look up a verbose version of the warning in perldiag or you can use the diagnostics
pragma in your code:
use diagnostics;
You can turn on warnings or diagnostics from the command line with the -M
switch. This might be useful if you can’t modify the code for some reason:
% perl -Mwarnings program
% perl -Mdiagnostics program
Perhaps the most hated of all warnings comes from uninitialized values. Here’s a program that uses two variables on the same line. One has a value and one is undef:
my $foo = 'foo'; my $bar; print "$foo $bar\n";
In versions before v5.10, you get the warning but still have to investigate the program to find out which variable was the problem:
% perl5.8.9 warning_test
Use of uninitialized value in concatenation (.) ...
foo
With v5.10 or later, Perl is smart enough to tell you:
% perl5.8.9 warning_test
Use of uninitialized value $bar in concatenation (.) ...
foo
Using more recent versions usually gives you better warnings.
After you get error or warning messages from perl, fix the first message then see if perl still issues the other messages. Those extra messages may be artifacts of the first problem.
Perl gives you warning messages when it gets worried and not before. By the time perl gets worried, the problem has already occurred and the line number perl is on may actually be after the problem. Look at the couple of expressions before the line number in the warning.
Don’t guess! Verify everything! Actually examine the value right before you want to use it in an expression. The best debugger in the universe is print:
print STDERR "The value is [$value]\n";
I enclose $value
in brackets so I can see any leading or trailing whitespace or newlines. If I have anything other than a scalar, I use Data::Dumper
to print the data structures:
require Data::Dumper; print STDERR "The hash is ", Data::Dumper::Dumper( \%hash ), "\n";
If the value is not what you think it is, back up a few steps and try again! Do this until you find the point at which the value stops being what you think it should be!
You can also use the built-in Perl debugger with perl‘s -d
switch. See perldebug for details:
% perl -d program.pl
You can also use other debuggers or development environments, such as ptkdb
(a graphical debugger based on Tk) or Komodo (ActiveState’s Perl IDE based on Mozilla). I cover debuggers in Chapter 4.
I have been programming Perl for quite a long time and I still look at perlfunc almost every day. Some things I just cannot keep straight, and sometimes I am so sleep-deprived that I take leave of all of my senses and wonder why sprintf()
does not print to the screen.
You can look up a particular function with the perldoc command and its -f
switch.
% perldoc -f function_name
If you’re using a module, check the documentation to make sure you are using it in the right way. You can check the documentation for the module using perldoc:
% perldoc Module::Name
Again, I constantly refer to perlvar. Well, not really since I find The Perl Pocket Reference much easier to use.
Some modules change behavior between versions. Do you have the version of the module that you think you have? You can check the installed module version with a simple perl one-liner:
% perl -MModule::Name -le 'print Module::Name-
VERSION'>
If you read most of your documentation off of the local machine, like at Perldoc, CPAN Search, or MetaCPAN, then you are more likely to encounter version differences in documentation.
If you’re trying something new, or think a particular piece of code is acting funny, write the shortest possible program to do just that piece. This removes most of the other factors from consideration. If the small test program does what it thinks it does, the problem probably isn’t in that code. If the program doesn’t do what you think it should, then perhaps you have found your problem.
Some things depend on environment variables. Are you sure that they are set to the right thing? Is your environment the same that the program will see when it runs? Remember that programs intended for CGI programs or cron jobs may see different environments than those in your interactive shell, especially on different machines.
Perl stores the environment in %ENV
. If you need one of those variables, be ready to supply a default value if it does not exist, even if only for testing.
If you still have trouble, inspect the environment.
require Data::Dumper; print STDERR Data::Dumper::Dumper( \%ENV );
If you have a problem, somebody else has probably already had that problem. See if one of those other people posted something to Stackoverflow or Perlmonks. The difference between people who ask questions and those who answer them is their ability to use the internet effectively.
If you want to track down the slow parts of the program, have you profiled it? Let Devel::SmallProf
do the heavy lifting for you. It counts the times perl executes a line of code as well as how long it takes and prints a nice report. I cover profiling in Chapter 5.
If you have a test suite, which test fails? You should be able to track down the error very quickly since each test will only exercise a little bit of code.
If you don’t have a test suite, why not make one? If you have a really small program or this is a one-off program, then I’m not going to make you write a couple of tests. Anything other than that could really benefit from some test programs. The Test::More
module makes this extremely simple, and if you program your script as a modulino as in Chapter 18, you have all the tools of module development available for your program.
Explain your problem aloud. Actually say the words.
For a couple of years I had the pleasure of working with a really good programmer who could solve almost anything. When I got really stuck, I would walk over to his desk and start to explain my problem. Usually I wouldn’t made it past the third sentence without saying “Never mind–I got it”. He almost never missed either.
Since you’ll probably need to do this so much, I recommend some sort of plush toy to act as your Perl therapist so you don’t annoy your colleagues. I have a small bear that sits on my desk and I explain problems to him. My wife does not even pay attention when I talk to myself anymore.
You have been staring at the computer screen, so maybe a different medium will let you look at things in a new way. Try looking at a print-out of your program.
Seriously. Perhaps you do not like those shows, so choose something else. Take a break. Go for a walk. Stop thinking about the problem for a bit and let your mind relax. Come back to the problem later and the fix may become immediately apparent.
If you’ve have made it this far, the problem may be psychological. You might be emotionally attached to a certain part of the code, so you do not change it. You might also think that everyone else is wrong but you. When you do that, you don’t seriously consider the most likely source of bugs–yourself. Do not ignore anything. Verify everything.
I have yet to meet a developer that doesn’t create bugs or struggle to track down a problem. The better programmers just do it with bigger and nastier bugs. Still, I find that sometimes I waste time doing really stupid things.
With multiple git clones and terminal sessions connected to different hosts, I’ve more than a couple times found myself editing files in one spot but not seeing the results in another spot. I keep running the program or the tests, but nothing seems to fix it. The print
statements I put in never some up in the output. When I realize that’s happening, I have to step back to make sure I’m actually running the version I think I am, that I’ve said the file I’m working on, and so on.
I mostly develop modules, so I check things by editing the module or test files. If I’m testing a particular issue, I usually run only that test file:
% perl -Mblib t/some_test.t
I like running tests like this so I can see the raw output without a TAP consumer getting in the way.
If I don’t run make
(or ./Build) again, my changes to the module files don’t show up in blib/
so my test file doesn’t see it. The tests continue to fail and I keep trying to make them pass. I need to run make again, so I add it before perl in one of these ways:
% make; perl -Mblib t/some_test.t
% make && perl -Mblib t/some_test.t
Sometimes I need to really start over, so I need to regenerate the Makefile
too:
% perl Makefile.PL && make && perl -Mblib t/some_test.t
I have many perls installed so I can test on different versions. Just like I need to verify that I’m running the right version of the source code, sometimes I need to check that I’m running the right perl. If I’m running a different perl, I might have different versions of modules installed. This is a rare situation, though, but it has happened to me.