"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.