In an old discussion on publishing bad examples of Perl 5 code, I left the other side of the issue unaddressed. If people who publish Perl code examples have a responsibility to publish good code (or at least to avoid publishing bad code), how do they discern between good and bad code? How do interested novices distinguish between good and bad examples?
The latter question has a flippant answer. If they could do so reliably, they would no longer be novices and would no longer have to worry about the question.
We can give them some hints, though.
Compiler Support
One of the reasons behind creating a CPAN distribution called Modern::Perl is to help
distinguish between Perl 5 code written for the pre-2000 era and Perl 5 code
written to take advantage of current features and best practices. Though
neither the presence of use Modern::Perl:
is a guarantee
that the remaining code is sufficiently good nor the absence of that line is
evidence that the remaining code is insufficiently modern, you can say
that most good examples of Perl 5 code circa 2013 do include:
use strict;
use warnings;
...or:
use Modern::Perl;
...or:
use 5.014;
use warnings;
k...or:
use Moose;
... or:
use common::sense;
... or something similar.
Without that safety net, it's easy to make inadvertent mistakes that
perl5
would otherwise happily warn you about. You can write great
code without them, but most of the great Perl programmers I know prefer to work
with that safety net. I make enough typos myself that
strict
saves me debugging time multiple times every week.
Perl as Perl
Perl's origins lie in system administration as a tool more flexible and powerful and wieldy than shell but also easier to use and manipulate than C. Perl is also available as part of the default system installation on many, many operating systems, and well-written Perl programs tend to be more portable than shell programs, as the Unix fragmentation wars manifested themselves in frequent skirmishes around shell and system utility syntax and features.
Consequently, a lot of Perl code in the wild is slightly more portable and slightly more featureful than shell scripting. That's a charitable way of saying that it reads like a shell script: bare-bones procedural code that calls lots of system programs and doesn't worry much about structure, just getting a job done quickly by gluing together a lot of other little programs.
That's all well and good in the sense that you can write good Perl code for those tasks, but there's a tendency to use Perl as merely Bourne shell with a better syntax, neglecting all of the language constructs and core library features to write maintainable, clear, effecient, effective, powerful, and useful programs.
This rule is necessarily fuzzy; I have a lot of useful, powerful,
maintainable programs that perform system()
calls or use
qx//
for very good reasons...
... but code sprinkled with calls to `ls`
and
`date`
and `sort`
and `grep`
without at
least a comment explaining why avoiding Perl's builtins in favor of external
utilities is necessary should trigger red flags in your mind.
The same goes for long listings of code without functions (or even obvious whitespace breaks between code paragraphs).
You can use Perl 5 as an improved shell, but you lose out on a lot of power of Perl.
Inelegant Code
Code doesn't evolve. Code either gets designed or code accretes. You can tell when code doesn't have a design ethos because it's all over the place. Even a simple system administration program of more than a couple of dozen lines can be more maintainable when it has sensible variable names and, yes, distinct functions.
I've seen monolithic CGI programs (it's always CGI programs, isn't it?) which use complicated nested conditionals to twist and turn and gyrate to display multiple distinct views.
I've seen the most awkward code written by people who couldn't make enough sense of what they wrote to distill a problem down into less than a few hundred lines of code.
(Some of the strangest code I've ever seen came from the keyboard of a
person who goes to great lengths to pass code to import
methods for delayed execution because, and I'm paraphrasing pretty closely
here, "Why should I have to put different classes in different files?" I'm all
for self expression, but the result is an undebuggable mess that doesn't look
like any Perl code I've ever seen anywhere else.)
I know it's a lot to ask of novice programmers, but when you find yourself wrestling with code and feeling like you're having to do things the hard way, step back for a moment. You're probably not solving a problem no one has ever solved before. You're probably solving a problem many of us have solved a hundred times before. There's probably an elegant way of getting from there to here.
If you're making a mess, stop. Take a deep breath. Then solve a small piece of the problem in an elegant way. Make sure you understand it. Then solve another small piece. Make both pieces work together elegantly. Make sure you understand that. Repeat until you've solved the problem.
I know elegance is a subjective measurement, but I can tell you about its absence. Inelegance is often the result of mockingbird copy and regurgitate programming, where you're scrabbling around with something that'll probably work if you can put the symbols in the right order. Also, it's midnight and you're wearing a blindfold and you're underwater in a cave.
Then again, a map
statement looks inscrutable to someone who
doesn't know that it's a little bit like a loop that you don't have to write
yourself and an object looks inscrutable to someone who doesn't know that it's
a way to draw fences around information and behavior and an aggregate data
structure looks inscrutable to someone who doesn't yet understand that names
exist solely for the humans and that doing a thing once isn't nearly as nice as
doing it innumerable times to things that don't have names.
... but the principle remains. You might not be able to say why certain code is messy and you might not currently have the skills to clean up the mess completely, but if you can cultivate the ability to discern elegance in code, you can at least know which example code to avoid.