Appendix B. brian’s Guide to Solving Any Perl Problem

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.

My Philosophy of Problem-Solving

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.

It is not personal

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.

Personal responsibility

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.

Change how you do things

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.

My Method

Does your program compile with strictures?

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.

What is the warning?

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.

Solve the first problem first!

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.

Look at the code before the line number in the error message!

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.

Is the value what you think it is?

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.

Are you using the function correctly?

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
Are you using the right special variable?

Again, I constantly refer to perlvar. Well, not really since I find The Perl Pocket Reference much easier to use.

Do you have the right version of the module?

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.

Have you made a small test case?

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.

Did you check the environment?

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 );
Have you checked Google?

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.

Have you profiled the application?

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.

Which test fails?

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.

Did you talk to the bear?

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.

Does the problem look different on paper?

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.

Have you watched the latest episode of Doctor Who or Sherlock?

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.

Have you packed your ego?

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.

Some Stupid Things I’ve Done and Still Do

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.

Am I editing the right file?

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.

Did I run make again?

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
Am I running the right perl?

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.