Home

Awesome

Perl style guide

A few guidelines for a 'post-modern' Perl coding style.

Work in progress. Suggestions and contributions are welcome!

Disclaimer

This is just another coding style guide and by no means an attempt to define a standard. Please don't take it as the ultimate truth.

If you don't agree with something on this guide, fine! I'll invite to make your very own one. Make sure to read the excellent Perl Best Practices book by Damian Conway.

The most important thing, after all, is that both you and your teammates are comfortable with a given guideline and stick to it.


Code layout

## Don't do this:
my $str =
  "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor aliqua.";

## Do this instead:
my $str = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor aliqua.";

## This is just wrong and a waste of time. Don't ever do this:
my $str = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
        . "tempor incididunt ut labore et dolore magna aliqua. Ut enim ad"   
        . "minim veniam, quis nostrud exercitation ullamco laboris nisi ut"  
        . "aliquip ex ea commodo consequat. Duis aute irure dolor in "       
        . "reprehenderit in voluptate velit esse cillum dolore eu fugiat"    
        . "nulla pariatur. Excepteur sint occaecat cupidatat non proident, " 
        . "sunt in culpa qui officia deserunt mollit anim id est laborum.";

## This is actually okay. Notice the EOL flag?
my $str = "Lorem ipsum dolor amet, consectetur adipisicing elit, sed do eiusmod tempor aliqua.\n"
        . "Ut enim ad minim veniam, quis nostrud exercitation laboris nisi commodo consequat.\n"
        . "Duis aute irure dolor in reprehenderit in cillum dolore eu fugiat nulla pariatur.\n"
        . "Excepteur sint occaecat non proident, sunt in culpa qui mollit anim id est laborum.";
## Good.
sub function {
  my ($arg, @args) = @_;
  for my $item (@args) {
    last if divides_by_zero $item;
    do_something $item;
  }
  $arg;
}

## Bad.
sub function {
    my ($arg, @args) = @_;
    for my $item (@args) {
        last if divides_by_zero $item;
        do_something $item;
    }
    $arg;
}
## Good.
sub function {
  if (test) {
    ...
  }
}

## Bad.
sub function
{
  if (test)
  {
    ...
  }
}
## Good.
my $self = {name    => 'Max Payne',
            age     => 43,
            weapon  => 'Beretta 92f'
            motives => [qw(revenge)]
            targets => ['Vladimir Lem', 'Vinnie Cognitti',
                        'Jack Lupino',  'Frankie Niagara']};

## Bad.
my $self = {
  name    => 'Max Payne',
  age     => 43,
  weapon  => 'Beretta 92f'
  motives => [qw(revenge)]
  targets => [
    'Vladimir Lem', 'Vinnie Cognitti',
    'Jack Lupino',  'Frankie Niagara'
  ]
};
## Notice that the last statement doesn't have a semicolon afterwards.
my $fact = sub { my ($n, $ret) = ($_[0], 1); $ret *= $_ for 1 .. $n; $ret }
my ( $self, $who, @params ) = @_;      # Bad.
my ($self, $who, @params) = @_;        # Good.

my $self = { name => "Eric", age => 26 };  # Bad.
my $self = {name => "Eric", age => 26};    # Good.

my @files = qx| ls $str |;             # Bad.
my @files = qx|ls $str|;               # Good.

Please keep in mind that this does not apply to operators.

my $fn = sub {$_[0] + 1};              # Bad.
my $fn = sub { $_[0] + 1 };            # Good.

my @items = map {do_something_to $_} @_;    # Bad.
my @items = map { do_something_to $_ } @_;  # Good.
## Good.
my $area = $pi * ($radius ^ 2);

## Bad.
my $area = $pi*($radius^2);
## Good
if (condition) {
  ...
}
else {
  ...
}

## Bad.
if (condition) {
  ...
} else {
  ...
}
my @months = qw(January   February March    April
                May       June     July     August
                September October  November December);

my $var_          = ...;
my $var_2         = ...;
my $var_long_name = ...;

my %h = (key1          => $val1,
         key_2         => $val2,
         key_long_name => $val3);
## A good example taken from Conway's PBP:
push @steps, $steps[-1] +
    $radial_velocity * $elapsed_time +
    $orbital_velocity * ($phase + $phase_shift) -
    $DRAG_COEFF * $altitude;

## The same example using our custom guideline:
push @steps, $steps[-1] +
             $radial_velocity * $elapsed_time +
             $orbital_velocity * ($phase + $phase_shift) -
             $DRAG_COEFF * $altitude;
say "I'll have a homemade ", is_it_summer ? 'ice cream' : 'hot chocolate';
my $variable = a_very_very_big_condition_name
                 ? is_this_very_long_subroutine
                 : is_that_one_that_is_even_larger;

my $var = cond_one               ? 'arg1'
        : cond_two               ? 'arg2'
        : cond_three_larger_name ? 'arg3'
        :                          'argn'
        ;
" Map F4 to a perltidy system call.
map <F4> !perltidy -q -gnu -i=2 -l=0 -pt=2 -bt=2 -sbt=2 -bbt=1 -nbl -vt=2 -vtc=2 -sct<CR>
" The same, but calling a configuration file.
map <F5> !perltidy -q -pro=/path/to/perltidyrc_file<CR>

Comments

Perl always had a good reputation documentation wise. Let's keep up the good work!

#### Module     : Useless module
#### Author     : elrzn <eric.lrzn@gmail.com>
#### Created on : 31 MAY 2012
package MyModule;

### Warning, evil function ahead!

## Does black magic. Highly unstable!
sub evil_function {
  ## Notice the vertical alignment in the following sentence...
  my @damned = map  { evil_stuff $_ }  # This is an inline comment, and
               grep { guiltyp $_    }  #  we continue it this way.
               process @_;
  wantarray ? @damned : [@damned];     # No explicit return in good old Perl fashion.
}
=head2 function_name : brief, optional description about types

This is a subroutine. It presumably does something.

Example:

  function_name %params;

=cut
sub function_name {
  ...
}

Coding style

## Bad.
sub my_method {
  my $self = shift;
  my %params = @_;
  ...
}

## Good.
sub my_method {
  my ($self, %params) = @_;
  ...
}
sub is_valid_p { ... }                 # Notice that the 'p' looks like '?'.

reboot_world if $finished_computing_p;

sub oddp { ... }

sub not_registered_p { ... }
## Multiple statements.
if ($test) {
  do_something;
  do_something_else;
  make_sure_you_did \&function, @args;
}

# One statement.
do_something if $test;

## One, large statement.
print "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor aliqua."
  if $can_print_p;
## Bad.
behaves_like_zombie_p $suspicious_stranger 
  and shoot $suspicious_stranger, target => ':head';

## Good.
shoot $suspicious_stranger, target => ':head' 
  if behaves_like_zombie_p $suspicious_stranger;
party_all_night if ! $sleepy;          # Bad.

party_all_night unless $sleepy         # Good.
my $million = 1_000_000;
my $query = <<SQL
  SELECT col1, col2
    FROM table_name
   WHERE year = 2012
     AND col1 LIKE '%foo%'
SQL

## This is even better for selecting blocks in certain text editors.
my $query = qq{
  SELECT col1, col2
    FROM table_name
   WHERE year = 2012
     AND col1 LIKE '%foo%'
};
## Correct, but bad style.
my %hash = (key1, 'val1',
            key2, 'val2',
            key3, 'val3');

## Good.
my %hash = (key1 => 'val1',
            key2 => 'val2',
            key3 => 'val3');

Object-oriented Programming

FIXME: I'm not so sure about this guideline.

package Class;
use Moose;
has 'a', is => 'ro', isa => 'Any';
has 'b', is => 'ro', isa => 'Any';

around BUILDARGS => sub {
  my ($orig, $class, @p) = @_;
  scalar @params == 2
    ? $class->$orig(a => $p[0], b => $p[1])
    : $class->$orig(@p);
}

...

1;

## Aim for an old school object constructor instead.
sub make_classname {
  Class->new(a => $_[0], b => $_[1]);
}
package ClassName;
use Moose;

## Rest of code here...

__PACKAGE__->meta->make_immutable;

1;

Recommended modules

use Readonly;
Readonly my $PI => 3.1415926;

Performance tips

Profiling