Perl Styleguide

Preface

If you want to learn about what to do in Perl and what to avoid, just read Perl-Best-Practices from Damian Conway. That excellent book is not only fun to read, it will tell you much more about Perl-programming than this short guideline could ever hope for.

Many recommendations from that book have actually been implemented in a Perl-module !PerlCritic. Pretty soon, OpenSLX will use this module to check Perl-code that has been commit to the OpenSLX repository (and reject it if it exhibits problems above a certain severity).

Introduction

The following paragraphs contain the conventions that shall be applied to OpenSLX Perl-code.

As there are quite a lot of rules, they have been split into different topics. Each entry is marked with a severity level - ranging from '!' (low) to '!!!' (high), with the most important rules being listed at the top of each topic.

Note: currently, these conventions are open for discussion, so please do not hesitate to ask for explanation or send improvements or comments - preferably to .

So, there you go:

Formatting

  • !!!: 4 space indents, no tabs in code (unless inside a string, of course)
  • !!!: 80 characters width per line. It's okay to exceed it for long string constants (but for nothing else).
  • !!!: Opening curly on the same line as the keyword it belongs to
  • !!!: Closing curly vertically aligned with the opening keyword
  • !!!: Space after comma or semi-colon, but not before
  • !!!: One Space between special keyword and its arguments: if (...) { ... }
  • !!!: Extra spaces in code delimiting curlies: sort { $a <=> $b } @foo
  • !!!: No space between keyword and its arguments if the "looks like a function, therefor it is a function" rule applies: print(join ',', @bars), not print (join ',', @bars).
  • !!!: Long lines: indent according to parens (or [], {}, etc), but always 4 spaces. Like this:
        $someLongVarName->thatInvokesAnInsanelyLongMethodNameTakingALotOfParams(
            'blurb', 1, sub {
                my ($attr) = @_;
                return lc($attr) eq $attr;
            },
            'more blurb'
        );
      
  • !!!: Long lines: continuing lines are indented
  • !!!: Long lines: Lines end before operator (especially = && ||)
  • !!!: No "outdent"s, no half indents and no double indents
  • !!!: No spaces around: -> (and only break lines before -> as a last resort)
  • !!!: Spaces around: =~ !~ * / % + - . << >> comparison_ops & | ^ && || ?: assignment_ops => and or xor
  • !!!: No space between ++/-- and the variable
  • !!!: Semi-colon never appears on its own line
  • !!: Opening curly for subs on the next line, i.e. write
        sub foo
        {
            if ($bar) {
                print "bar\n";
            }
        }
      
  • !!: Uncuddled else:
        } 
        else {
      
  • !!: ? and : begin lines in complex expressions (and get indented, since a long line is being broken)
  • !!: No extra spaces around or inside parens: foo, (bar, baz), quux
  • !!: Extra spaces in arrayref constructor: [ foo, bar ]
  • !!: Extra spaces in hashref constructor: { foo => bar }
  • !!: No parens unless needed for clarity
  • !!: No double spaces except for vertical alignment and comments
  • !!: Empty line between logical code chunks
  • !!: No space between array/hash and index/key: $foor0, $foo{bar}
  • !!: Complex regexes get /x (and are nicely formatted and preferably get comments, too)
  • !: No double empty lines
  • !: No quotes for simple literal hash keys
  • !: Space around index/key if it is complex: $foo{ $bar{baz}{bar} }
  • !: Subs after main code in scripts
  • !: Semi-colon only left out for implicit return or in single-statement block
  • !: in every sub, separate argument shifting and implementation by a single line (and do the same for the final return statement):
        sub showMsgIf
        {
            my ($msg, $func, $arg) = @_;
    
            return if !$func->($arg);
            print $msg, "\n";
    
            return 1;
        }
      

Names

  • !!!: code in English (identifiers, methods and comments)
  • !!!: Multi-word identifiers use (lower)camelCase (i.e. $thisIsSomeVar and $self->callAMethod();
  • !!!: Lowercase identifiers, but uppercase for constants
  • !!!: Never use $_ unless it is required (i.e. in map and grep) - especially use explicit vars in loops:
        foreach my $attr (@attributes) {
            ...
        }
      
  • !!!: No single-letter identifiers ($a, $b or $i) except when sorting or as an index
  • !!!: $i is a(n index) counter
  • !!!: Give variables meaningful names that communicate their meaning: $key and $value are pretty useless - name them for what they are (like: $attrName or $attrVal if you must)
  • !!!: Data type not represented in variable name: %foo and @foo, but not %foo_hash or @foo_array. However:
    • data type of referent in reference variable names: $bla_hash is okay
    • data type ref in reference variable names: $hashref is okay
  • !!!: Dummy variables can be called foo, bar or anything else (but you don't really need those, do you?)
  • !!: No abbreviations (acronyms are okay, and so are VERY common abbreviations), NEVER @ary

Perl-stuff

  • !!!: use strict and use warnings. Loading of normal modules comes after loading these two.
  • !!!: No eval STRING unless absolutely required, use eval {} instead
  • !!!: Always Class->method, never method Class (this includes new!)
  • !!!: No grep EXPR and map EXPR, always use a BLOCK: grep {} @foo and map {} @bar
  • !!!: Use => (fat comma) in hashes, use , (comma) in all other places
  • !!!: Use lexical filehandles (my $inFH instead of IN)
  • !!!: use base 'BaseClass' instead of use [[BaseClass]] and setting @ISA
  • !!!: Localization of globals if they're to be changed (especially with $/)
  • !!!: Use {} as delimiter for regexes and tr: $var =~ s{^this}{that}igms; - alternatively use [] (but please avoid //)
  • !!!: True and false are always implied. No $foo 0 when testing for truth.
  • !!!: Don't be afraid of postfix if/unless, use them for oneliners if the "other" statement is the important thing:
    prefer
        return unless $haveParam;
      
    over
        unless ($haveParam) {
            return;
        }
      
    and
        print "trying $distroModule" if $verbose;
      
    over
        if ($verbose) {
            print "trying $distroModule";
        }
      
    but prefer
        open(my $configFH, '<', $configFile)
            or die "unable to open $configFile ($!)";
      
    over:
        die "unable to open $configFile ($!)" 
            unless open(my $configFH, '<', $configFile);
      
  • !!!: Never pair unless with !, use if instead.
  • !!: Explicit returns from subs
  • !!: Logical order in comparisons: $foo 4, but never 4 == $foo
  • !!: Use here-documents for multi-line strings (if they exceed two lines), use unshiftHereDoc() (from OpenSLX::Utils) to unindent them.
  • !!: Single quotes when double-quote features not used
  • !!: Sane variable scopes (declare vars as late as possible)
  • !!: No globals when access from another package is not needed
  • !!: Whatever tool is useful: no OO when it does not make sense
  • !!: It's okay to import symbols
  • !!: Prefer && || ! over and or not, unless the lower precedence is required (as in open($inFH, '<', $filename) or die "bummer - no $filename!"
  • !!: Guards (return if ...) are nicer than deep else-blocks
  • !!: Always check return values where they are important (if in doubt: do check!)
  • !!: Declare variables on first use, not before (unless required)
  • !!: List assignment for sub arguments if there are many (>2), not lots of shifts:
        sub foo
        {
            my ($bar, $doh, $pir) = @_;
            ...
        }
      
    but this is ok:
        sub show
        {
            my $msg = shift;
            ...
        }
      
  • !: Error checking is done using or. This means 'open or do { ... }' instead of 'unless (open) { ... }' when handling the error is more than a simple statement.
  • !: 'our $VERSION', not 'use vars qw($VERSION);'
  • !: Direct '@_' access is okay in very short subs

Comments

  • !!!: Comments where code is unclear
  • !!!: Comments usually explain the WHY, not the HOW
  • !!: Block comments precede the code block they relate to
  • !!: Comments that follow code on the same line have four spaces before and one after the '#' symbol (unless vertically aligned with other comments above/below). If those comments need more space, they continue on the next line, but are vertically aligned:
    --------------------------------------------------------------------------------
             1         2         3         4         5         6         7         8
        ...
        return if $vomittingHorse;    # I know it is unlikely, but nevertheless we
                                      # should check for it!
        ...