"Its not about the acid in the paper, its about the Crack in the author."

Seems like everyone is writing a Perl book. The most disturbing part is that they're being written by people who have nothing to do with Perl. How to decide what's crap and what's not?

Worry no more! After many mirth-filled hours of flipping through many an awful Perl book, I have come up with a simple one-minute litmus test to determine if the book you're holding is worth the tree its printed on.

Historical Note This page was written back in 1999 when the flood of Perl books hit the market.

Editor's Note Typos in the text quoted from books are likely my own unless otherwise specified.

The Perl Book Litmus Test

Remember, the point of this test is to find bad books and there can only be negative results with this test. A book which passes all the tests put forth here CAN STILL SUCK.

The following tests check the five things books and beginning Perl programmers most commonly screw up. Its by no means intended to be canonical, just a quick way to look for read flags. So flip to the index. Look up the following tidbits and answer the questions.

localtime
Due to localtime's ahem "vintage" interface, date generation is usually botched in Perl. Its important that a book has a good discussion of localtime and its caveats. Does it state that it returns the number of years since 1900? Does it mention that when used in scalar context it returns a nicely formated date? Does it avoid things like `date`?
open || die $!
"Why doesn't $line = <FILE> work?" is one of the most common newbie questions. Its extremely important that a book drills it into the reader's head that all system calls should be checked and proper error messages returned. This means putting some sort of error checking on all system calls (not just open()), using and discussing $! and other good error messages.
srand
Not a common problem, but something often gotten wrong. If a book does drag out srand(), it often fails to point out that it should be called only once. (If srand is never mentioned, that's okay.)
array size
Does it clearly say that an array will return its number of elements in scalar context, or does it use/imply $num = $#array + 1;
flock
Any CGI program writing to a file is going to run into file corruption issues pretty fast. If the book covers topics which will lead to concurrent file access, it should talk about flock(). Does it discuss and use flock instead of lockfiles? (ie. setting some .lock file instead of using flock()).
Portable Constants
When performing flocking, socket operations or sysopens does it use the constants defined by Perl (LOCK_SH and friends), or do they define their own unportable constants? If the subject never comes up that's ok.

Now, let's see how a few books fare...

CGI For Commerce by Gunther Birznieks and Selena Sol (1997)
localtime: FAIL.
This one takes the cake. It doesn't just have a Y2K bug, it has a severe case of Cargo-Cult. The authors use the date formatting code from Matt Wright's programs and attempt to fix the non-existent Y2K bug in Perl!
($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time); The the script formats the variables and assign them to the final $date variable. Note that $sc_current_century is defined in web_store.setup. Since the 20th century is really 1900-1999, we will need to subtract 1 from this value in order to format the year correctly. if ($hour < 10) { $hour = "0$hour"; } if ($min < 10) { $min = "0$min"; } if ($sec < 10) { $sec = "0$sec"; } $year = ($sc_current_century-1) . "$year"; $date = "$days[$wday], $months[$mon] $mday, $year at $hour\:$min\:$sec"; return $date; page 252

GAH! I have to tell it what CENTURY it is?! I can see it now, One-minute to Midnight, New Years Eve 1999. Selena Sol sits in a cold machine room while the party of the millennium goes on outside. The clock strikes midnight as they frantically begin changing $sc_current_century in all their programs.

And the punchline is that the code doesn't even work! The above will produce 20100 in 2000. Minus 1 million points for not setting your clock forward and trying the code

srand: Not mentioned.
Array size: Pass
flock: FAIL
Uses lock files created in a non-atomic fashion.
Constants: Not covered.

Perl 5 Unleashed by Kamran Husain (1996)
localtime: FAIL
Uses `date` to get a formatted date string.
srand: FAIL
Never mentions it should be used only once.
Array size: Pass
flock: Never discussed.
Constants: FAIL
Defines its own AF_UNIX and AF_INET constants.

Perl 5 By Example by David Medinets (1996) (Sorry, Dave)
localtime: FAIL
"$year does not handle centuries."
srand: Pass
Array size: Pass
flock: Pass
Constants: Pass

Perl 5 Quick Reference by Mícheál Ó Foghlú (1996)
localtime: FAIL
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(); print "localtime() 19$year-$mon-$mday\n"; page 216
srand: Pass
Array size: Pass
flock: Pass
Constants: Not discussed.

Perl 5 Interactive Course, Certified Edition by Jon Orwant (1998)
Tested by ology.

PH 7!

localtime: Pass
srand: Pass
Array size: Pass
flock: Pass
Constants: Pass

Essential Perl 5 For Web Professionals by Brown, Bellew, and Livingston(1999)
Tested by ology.

localtime: FAIL
($sec, ... ) = localtime(time); $date = $mon+1 . "/" . $mday . "/" . $year; page 92
Array size: FAIL
No mention at all.
srand: Pass
flock: Pass
Constants: Pass

Perl and CGI for the World Wide Web by Castro (1999)
Tested by ology.

localtime: FAIL
No mention of year being returned as those since 1900 and no mention of generating a string date in scalar context.
srand: Never mentioned
Array size: Pass
flock: Pass
Constants: Never mentioned
SPECIAL ACID
This book refers the reader to Matt's Script Archive and Selena Sol.

Perl For Dummies 2nd Edition by Paul Hoffman, IDG Books 1998
localtime: Pass
The use of localtime() is fairly well covered (issues raised above aside) However, I found a y2k bug in one of its example programs (page 165):
# Get the time right now into a bunch of variables ($Second, $Minute, $Hour, $DayOfMonth, $Month, $Year, $WeekDay, $DayOfYear, $IsDST) = localtime(time); $RealMonth = $Month + 1; # Since $Month starts at 0 #Format the variables to be two digits long $DateAndTime = sprintf("%02d/%02d/%02d\t%02d:%02d:%02d", $RealMonth, $DayOfMonth, $Year, $Hour, $Minute, $Second);
ooopise, forgot to take its own advice about $Year.
Array size: FAIL!
The notion of "lists" and "arrays" is confused in the book, often referring to @array as a list... but that's forgivable in a newbie book. However, it does fail: page 101
Perl makes it easy to find out how many elements are in a list without having to count each of them. The variable $#listname is the number of the last element in a list. So, for the four-element shopping list I've been using in this chapter, entering print $#Shop; displays the number 3 Note that this is not the length of the list; it is the number of the last element. This length of the list is actually that number plus 1."
flock: FAIL
Never mentioned through the book, even though writing to files and CGI programming are covered.
open || die $!: FAIL
There's a section entitled "Determining if a file really opened" but $! is never mentioned. To make matters worse, the book doesn't follow its own advice and rarely uses "open() or die" in its examples. In its unlink() example, it DOES check!
unless( unlink('a.dat', 'b.dat') == 2 ) { print "One of the files didn't get deleted.\n" }
but too little, too late. $! isn't used, the unlink'd filenames are not reported in the diagnostic and its printing error messages to STDOUT.
srand: FAIL
Its mentioned, but not explained that it should be called only once and never explained that it can be omitted entirely.
constants: Never mentioned

Perl Core Language: Little Black Book by Steven Holzner, Coriolis Technology Press 1999, 492 pages.
A book that's pushing 500 pages is hardly "little". This book seemed to have a hard time deciding if it was going to be a reference or a tutorial or a cookbook.

localtime: FAIL
"localtime", "gmtime", "y2k" and "date" don't appear at all in the index and don't seem to be covered anywhere in the book! time() is covered and it does mention that the MacOS epoch is 1904 instead of 1970. Points there. (page 236)
Array size: Pass
Getting the length of an array is properly covered but it commits a small teaching mistake. Just before talking about scalar(@array) it says (page 62):
You could display the number of elements in this array by adding 1 to $#array: @array = (1, 2, 3); print "\@array has ". ($#array + 1) . " elements.";
And then goes on to explain about arrays in scalar context. However, a reader looking up how to find the length of an array will probably stop before getting to that point (having gotten a solution to his problem) and go back to coding. Murphy strikes again.
flock: Pass
Constants: Pass
open || die $!: FAIL
open is covered, along with a probably unnecessary full page about +>, >&, >-, +>> and the rest of that salad. Even though "open() or die" style is used in a quick example, its never explained why, and $! is not used in the die message. (pages 265-266) unlink() is used without checking its success. (page 280)
srand: FAIL
only briefly covered and its never mentioned that its to be used only once. (page 235)

PERL: Black Book by Steven Holzner, CoriolisOpen Press 1999 1283 Pages
This is the big brother to the "Little Black Book" and its 1300 PAGES! Now we know why the other one was called "Little".

localtime: FAIL
"gmtime", "date" and "y2k" are not mentioned in the index. The only mention of localtime() I could find was on page 444 in a section about $^T! In there it does mention that localtime() in scalar context will produce a date, but its in such an obscure location that I have to fail it.
Array size: Pass
Getting the length of an array is covered exactly the same way as in its little cousin and it repeats exactly the same mistakes. Of course, given that its nearly three times as large it has to add something, so it adds another mistake: (page 101)
TIP: So, how do you clear an array? Just set $#array to -1.
Gads! Please, @array = (); is much less arcane. It passes, but barely.
flock: Pass
constants: Pass
open || die $!: FAIL
Same coverage as the little book, same mistakes. unlink(), too.
srand: FAIL
Its soooo close! (Page 502)
"I have a problem," the novice programmer says, "I'm using the random number function rand, but it keeps generating the same sequence of numbers each time I run my program." "Well," you say, "you can call the srand function to seed the random number generator before using rand." "Hmm," the NP says, "why doesn't Perl do that automatically?" "It does," you say, "in any version after Perl 5.004. Time to upgrade, NP."
And at this point you maybe show: srand if $[ > 5.004; but it doesn't, and that's disappointing because it was a perfect set up. Also, the line "you can call the srand function to seed the random number generator before using rand" leads to reader to think, "Okay, so this will work..." while(...) { srand; $rand = rand; }

PERL and CGI For The World Wide Web by Elizabeth Castro, Peachpit Press 1999 272 pages
This is not a bad book. The style is very clean and visual, it has a light touch with color and highlighting to bring out the important bits. The text is offset next to screenshots of what is being taught. I'm cheering for this one to pass.

The scope of this book (with respect to Perl) is rather limited. Its directed at a web designer/HTML guy who wants to add some simple programs to their page. (Page 21)

You should know that this book is not--nor does it try to be--an exhaustive guide to Perl. Instead, it is a beginner's guide to using Perl for one particular purpose--making Web pages interactive.

We'll try to keep that in mind while tearing it apart.

localtime: FAIL
The index has no reference to "localtime", "gmtime", "date" or "y2k". The entry for "time" points us off to a quick discussion about how to display the current time using localtime() and sprintf() (page 188). This is an unfortunate oversight, because next to "how do I write a hit counter" the first thing people want to do is put the current time *and* date on their page.
Array size: Pass
Array lengths are well handled (page 85). Even the difference between ($variable) = @array; and $variable = @array; is covered.
flock: Pass
It only talks about flock()ing when writing, not reading. However, this oversight is forgivable as at least the file will not be corrupted.
constants: FAIL
Unfortunately, numbers are used instead of LOCK_*.
open || die $!: FAIL
open() || &ErrorMessage; is covered (page 192) and consistently used throughout the book, however $! is never mentioned and the error message printed is a static string, not even containing the name of the file or any useful information.
srand: Not mentioned.

Web Programming with Perl 5 by Bill Middleton, Brian Deng & Chris Kemp Sams.net Publishing 1997, 365 pages
This is a fairly horrid book. It contains lots of nasty, incomprehensible example programs and atrocious style. Code is smashed together, indenting is nonexistent (I think the author's Tab key was broken) and whitespace is nowhere to be seen. Totally unreadable. This was not written by a Perl programmer.

The book does state in its introduction that it does assume the reader knows the basics of Perl (but apparently the author is exempt), so we'll adjust accordingly while testing.

localtime: FAIL
Dates and localtime() are not covered at all as far as I can tell. An example calendar program contained this gem (page 221)
require 'httools.pl'; &date; # Calls the todays date subroutine from httools.pl $this_month=$month; [$month and $year must have come from &date, its $this_year=$year; MAaaaaGic! --Schwern] ($junksec,$junkmin,$junkhour,$today_day,$today_month,$today_year,$junkwday,$junkyday,$junkisdst)=localtime(time);$today_year+=1900;$today_month+=1;
Did you see it? Yep, somewhere in that mess is $today_year+=1900! Sorry, no cigar. Here's another fun one:
if (($this_year%4)==0 && ($this_month>2)) {$days_offset++}; # Current year is leap year.
While this won't cause any problems until 2100, its quite unforgivable to publish bad algorithms under the premise of "nobody will be using this code in N years anyway".
array size: FAIL
I can't find anything in the index talking about arrays or lists. It misnames arrays as "scalar arrays" which seems to me to be either contradictory or redundant, depending on how you look at it. A look at this example shows more horrors.
$month_image=@month_images[$this_month-1];
I couldn't find an example of it using a list in scalar context, but I'm going to assume that a horror is in there somewhere waiting to get out and fail it on general principles.
flock: FAIL
flock() is not mentioned at all. Given all the writing and reading and CGI stuff this book is doing, flocking is a very important thing to cover.
constants: Never mentioned
open || die $!: Pass
srand: Never mentioned

Teach Yourself Perl in 24 Hours by Clinton Pierce, SAMS Publishing 2000, 431 pages
Despite the title and publisher, this is a fairly decent work. The author is a friend of mine and I did a little tech editing on it. So I'll be COMPLETELY objective. ;)

PH 7!

localtime: Pass with flying colors!
In its discussion about localtime(), it says:
($sec, $min, $hour, $mday, $mon, $year_off, $wday, $yday, $isdst) = localtime;
A very important distinction is made there, using $year_off (year offset) instead of $year. The text about $year_off says:
$year_off Number of years since 1900. Add 1900 to this number for the correct 4-digit year.
And follows all this up with a box about the evils of "19$year". It also mentions that print scalar(localtime); will give you the formatted date.
array size: Pass
flock: Pass with flying colors!
The book even goes one step further, showing how to write a general purpose semaphore-based locking system to avoid the problem of the non-atomic nature of open() + flock().
constants: Pass
srand: Never mentioned
rand() is only mentioned in passing and srand() never discussed. That's fine.

Teach Yourself Perl in 21 Days by Laura Lemay, SAMS Publishing 1999, 680 pages
Okay, so if SAMS can teach you Perl in 24 hours, how about 3 weeks?

localtime: FAIL
Their gmtime() documentation (page 578) says:
The year returned is the number of years since 1900, not simply the last two digits of the year, thus avoiding the dreaded year 2000 problem.
Boy, I sure am glad there's no y2k problems with Perl programs! While their statement is correct, the lack of further discussion leads the reader into thinking they're safe from y2k problems.
array size: Pass
"Finding the Length of an Array" on page 78 hits the nail right on the head.
flock: FAIL
Not discussed, even though it has sections on files and CGI programming.
constants: Never mentioned
open || die $!: Pass
srand: Pass
The appendix clearly states, "You should use it only once in your program."

Mastering Perl 5 by Eric C. Herrmann, SYBEX Inc 1999, 926 pages
This is a truly massive tome. Whole forests were leveled printing this book. Truly "Perl By The Pound".

localtime: FAIL
Wow, did the author ever write any Perl?
The array that is returned by the localtime function is time formatted in the order shown here: ... * The year in four-digit format
array size: FAIL
Dear Lord!
You can calculate the size of an array using the last cell index variable, with this formula: $#arrayName - $[ + 1;
$[ is dragged out with no explanation. No where else is it mentioned in the book. Sorry, you lose for even brining up $[ in a beginner's book.
However, there's an easier way. You can create an array in scalar context, like this: $arraySize = @array;
Create an array in scalar context? Wuh?
open || die $!: FAIL
Your program can fail to open a file because it doesn't have the correct permissions, the file doesn't exist, the path to the filename is incorrect, or something else is wrong, so I recommend using syntax like this: open (FILEHANDLE, "$fileName") || die "$!, couldn't open file $fileName"; The die function and special variable $! give you error information from the Perl 5 interpreter, as you learned in Chapter 2."
However, this is not mentioned for any other file operations. unlink() and seek() are used extensively in example code without ever checking their success.
flock: Pass
Discussed extremely well, giving it three pages and even consistently using flock || warn $!;
constants: Pass
srand: Never mentioned
Random numbers never come up, even though the cover says "Complete Coverage of Perl 5" Oh well, good enough.

PERL Programmer's Reference by Martin C. Brown, Osborne/McGraw-Hill 1999, 380 pages.
This is a little reference book. It appears to be very complete in its coverage of the core, including even less.pm (but failing to note that its a joke).

localtime: Fail
The localtime() documentation is extremely bare, just listing the familiar [page 70]
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
with no mention of the caveats associated with $mon and $year.
Array size: Pass
Covered on page 2.
open || die $!: FAIL
The open documentation states:
You should not ignore failures to the open command, so it is usually used in combination with warn, die, or a control statement.
but no example is given and $! is not mentioned.
flock: Pass
constants: Pass
srand: FAIL
Mentioned, but not that it should be called once.

Elements of Programming with Perl by Andrew L. Johnson, Manning Publications 2000, 352 pages
I've heard good things about this book. And I feel really bad about failing it, because its not a bad book. It just omitted a few things.

localtime: FAIL
Oddly enough, none of localtime, gmtime, time, "date" or "y2k" appear in the index. I can't find any mention of dates in the table of contents, and flipping through the book it doesn't seem to come up anywhere. Given that this is a book for first time programmers, and how often dates come up when coding, and how often they're screwed up, I'm going to have to fail it for this oversight.
array size: FAIL
I can't seem to find any discussion of $# or array size or an array's behavior in scalar context. Again, I have to fail it for the oversight.
open || die $!: Pass
flock: Never mentioned.
constants: Never mentioned.