What are some elegant features or uses of Perl?

What? Perl Beautiful? Elegant? He must be joking!

It's true, there's some ugly Perl out there. And by some, I mean lots. We've all seen it.

Well duh, it's symbol soup. Isn't it?

Yes there are symbols. Just like 'math' has 'symbols'. It's just that we programmers are more familiar with the standard mathematical symbols. We grew to accept the symbols from our mother languages, whether that be ASM, C, or Pascal. Perl just decided to have a few more.

Well, I think we should get rid of all the unnecessary symbols. Makes the code look better.

The language for doing so already exists. It's called Lisp. (and soon, perl 6.)

Okay, smart guy. Truth is, I can already invent my own symbols. They're called functions and methods. Besides, we don't want to reinvent APL.

Oh, fake alter ego, you are so funny! It's really true, Perl can be quite beautiful. It can be quite ugly, as well. With Perl, TIMTOWTDI.

So, what are your favorite elegant bits of Perl code?

Answers


Perl facilitates the use of lists/hashes to implement named parameters, which I consider very elegant and a tremendous aid to self-documenting code.

my $result = $obj->method(
    flux_capacitance       => 23,
    general_state          => 'confusion',
    attitude_flags         => ATTITUDE_PLEASANT | ATTITUDE_HELPFUL,
);

My favourite pieces of elegant Perl code aren't necessarily elegant at all. They're meta-elegant, and allow you to get rid of all those bad habits that many Perl developers have slipped into. It would take me hours or days to show them all in the detail they deserve, but as a short list they include:

  • autobox, which turns Perl's primitives into first-class objects.
  • autodie, which causes built-ins to throw exceptions on failure (removing most needs for the or die... construct). See also my autodie blog and video).
  • Moose, which provide an elegant, extensible, and correct way of writing classes in Perl.
  • MooseX::Declare, which provides syntaxic aweseomeness when using Moose.
  • Perl::Critic, your personal, automatic, extensible and knowledgeable code reviewer. See also this Perl-tip.
  • Devel::NYTProf, which provides me the most detailed and usable profiling information I've seen in any programming language. See also Tim Bunce's Blog.
  • PAR, the Perl Archiver, for bundling distributions and even turning whole programs into stand-alone executable files. See also this Perl-tip.
  • Perl 5.10, which provides some stunning regexp improvements, smart-match, the switch statement, defined-or, and state variables.
  • Padre, the only Perl editor that integrates the best bits of the above, is cross-platform, and is completely free and open source.

If you're too lazy to follow links, I recently did a talk at Linux.conf.au about most of the above. If you missed it, there's a video of it on-line (ogg theora). If you're too lazy to watch videos, I'm doing a greatly expanded version of the talk as a tutorial at OSCON this year (entitled doing Perl right).

All the best,

Paul


I'm surprised no one mentioned the Schwartzian Transform.

my @sorted =
  map  { $_->[0] }
  sort { $a->[1] <=> $b->[1] }
  map  { [ $_, expensive_func($_) ] }
@elements;

And in the absence of a slurp operator,

my $file = do { local $/; readline $fh };

Have a list of files the user wants your program to process? Don't want to accidentally process a program, folder, or nonexistent file? Try this:

@files = grep { -T } @files;

And, like magic, you've weeded out all the inappropriate entries. Don't want to ignore them silently? Add this line before the last one:

warn "Not a file: $_" foreach grep { !-T } @files;

Prints a nice warning message for every file that it can't process to standard error. The same thing without using grep would look like this:

my @good;
foreach(@files) {
  if(-T) {
    push @good, $_;
  } else {
    warn "Not a file: $_";
  }
}

grep (and map) can be used to make code shorter while still keeping it very readable.


The "or die" construct:

open my $fh, "<", $filename
    or die "could not open $filename: $!";

The use of qr// to create grammars:

#!/usr/local/ActivePerl-5.10/bin/perl

use strict;
use warnings;
use feature ':5.10';

my $non_zero         = qr{[1-9]};
my $zero             = qr{0};
my $decimal          = qr{[.]};
my $digit            = qr{$non_zero+ | $zero}x;
my $non_zero_natural = qr{$non_zero+ $digit*}x;
my $natural          = qr{$non_zero_natural | $zero}x;
my $integer          = qr{-? $non_zero_natural | $zero}x;
my $real             = qr{$integer (?: $decimal $digit)?}x;

my %number_types = (
    natural => qr/^$natural$/,
    integer => qr/^$integer$/,
    real    => qr/^$real$/
);

for my $n (0, 3.14, -5, 300, "4ever", "-0", "1.2.3") {
    my @types = grep { $n =~ $number_types{$_} } keys %number_types;
    if (@types) {
        say "$n is of type", @types == 1 ? " ": "s ", "@types";
    } else {
        say "$n is not a number";
    }
}

Anonymous subroutines used to factor out duplicate code:

my $body = sub {
    #some amount of work
};

$body->();
$body->() while $continue;

instead of

#some amount of work
while ($continue) {
    #some amount of work again
}

Hash based dispatch tables:

my %dispatch = (
    foo => \&foo,
    bar => \&bar,
    baz => \&baz
);

while (my $name = iterator()) {
    die "$name not implemented" unless exists $dispatch{$name};
    $dispatch{$name}->();
}

instead of

while (my $name = iterator()) {
    if ($name eq "foo") {
        foo();
    } elsif ($name eq "bar") {
        bar();
    } elsif ($name eq "baz") {
        baz();
    } else {
        die "$name not implemented";
    }
}

Three-line classes with constructors, getter/setters and type validation:

{
    package Point;
    use Moose;

    has ['x', 'y'] => (isa => 'Num', is => 'rw');
}

package main;
my $point = Point->new( x => '8', y => '9' );

$point->x(25);

A favorite example of mine is Perl's implementation of a factorial calculator. In Perl 5, it looks like so:

use List::Util qw/reduce/;
sub factorial {
    reduce { $a * $b } 1 .. $_[0];
}

This returns false if the number is <= 1 or a string and a number if a number is passed in (rounding down if a fraction).

And looking forward to Perl 6, it looks like this:

sub factorial {
    [*] 1..$^x
}

And also ( from the blog in the link above ) you can even implement this as an operator:

sub postfix:<!>(Int $x) {
    [*] 1..($x || 1)
}

and then use it in your code like so:

my $fact5 = 5!;

If you have a comma separated list of flags, and want a lookup table for them, all you have to do is:

my %lookup = map { $_ => 1 } split /,/, $flags;

Now you can simply test for which flags you need like so:

if ( $lookup{FLAG} ) {
    print "Ayup, got that flag!";
}

I am surprised no one has mentioned this. It's a masterpiece in my opinion:

#!/usr/bin/perl
                                           $==$';
                                         $;||$.| $|;$_
             ='*$ (                  ^@(%_+&~~;# ~~/.~~
         ;_);;.);;#)               ;~~~~;_,.~~,.* +,./|~
    ~;_);@-,  .;.); ~             ~,./@@-__);@-);~~,.*+,.
  /|);;;~~@-~~~~;.~~,.           /.);;.,./@~~@-;.;#~~@-;;
  ;;,.*+,./.);;#;./@,./        |~~~~;#-(@-__@-__&$#%^';$__
   ='`'&'&';$___="````"  |"$[`$["|'`%",';$~=("$___$__-$[``$__"|
              "$___"|       ("$___$__-$[.%")).("'`"|"'$["|"'#").
        '/.*?&([^&]*)&.*/$'.++$=.("/``"|"/$[`"|"/#'").(";`/[\\`\\`$__]//`;"
        |";$[/[\\$[\\`$__]//`;"|";#/[\\\$\\.$__]//'").'@:=("@-","/.",
       "~~",";#",";;",";.",",.",");","()","*+","__","-(","/@",".%","/|",
        ";_");@:{@:}=$%..$#:;'.('`'|"$["|'#')."/(..)(..)/".("```"|"``$["|
        '#("').'(($:{$'.$=.'}<<'.(++$=+$=).')|($:{$'.$=.'}))/'.("```;"|
        "``$[;"|"%'#;").("````'$__"|"%$[``"|"%&!,").${$[};`$~$__>&$=`;$_=
       '*$(^@(%_+&@-__~~;#~~@-;.;;,.(),./.,./|,.-();;#~~@-);;;,.;_~~@-,./.,
        ./@,./@~~@-);;;,.(),.;.~~@-,.,.,.;_,./@,.-();;#~~@-,.;_,./|~~@-,.
          ,.);););@-@-__~~;#~~@-,.,.,.;_);~~~~@-);;;,.(),.*+);;# ~~@-,
           ./|,.*+,.,.);;;);*+~~@-,.*+,.;;,.;.,./.~~@-,.,.,.;_)   ;~~~
             ~@-,.;;,.;.,./@,./.);*+,.;.,.;;@-__~~;#~~@-,.;;,.*   +);;
               #);@-,./@,./.);*+~~@-~~.%~~.%~~@-;;__,. /.);;#@-   __@-
                 __   ~~;;);/@;#.%;#/.;#-(@-__~~;;;.;_ ;#.%~~~~  ;;()
                      ,.;.,./@,.  /@,.;_~~@- ););,.;_   );~~,./  @,.
                      ;;;./@,./|  ~~~~;#-(@- __,.,.,.    ;_);~~~ ~@
                       -~~());;   #);@-,./@,  .*+);;;     ~~@-~~
                       );~~);~~  *+~~@-);-(   ~~@-@-_      _~~@-
                       ~~@-);;   #,./@,.;.,    .;.);@      -~~@-;
                       #/.;#-(   ~~@-@-__      ~~@-~~       @-);@
                       -);~~,    .*+,./       |);;;~        ~@-~~
                        ;;;.;     _~~@-@     -__);.         %;#-(
                        @-__@      -__~~;#  ~~@-;;          ;#,.
                        ;_,..         %);@-,./@,            .*+,
                        ..%,           .;.,./|)             ;;;)
                        ;;#~            ~@-,.*+,.           ,.~~
                       @-);            *+,.;_);;.~         ~););
                      ~~,.;         .~~@-);~~,.;.,         ./.,.;
                      ;,.*+        ,./|,.);  ~~@-         );;;,.(
                    ),.*+);                              ;#~~/|@-
                  __~~;#~~                                $';$;;

I absolutely love Black Perl (link to version rewritten to compile under Perl 5). It compiles, but as far as I can tell it doesn't actually do anything.

That's what you get for a language written by a linguist from a pragmatic perspective rather than from a theoretical perspective.

Moving on from that, you can think about the Perl that people complain about as pidgin Perl (perfectly useful, but not expressive, and beware of trying to express anything complex in it), and the stuff that @pjf is talking about as "proper" Perl, the language of Shakespeare, Hemingway, Hume and so on. [edit: err, though easier to read than Hume and less dated than Shakespeare.] [re-edit and hopefully less alcoholic than Hemingway]


Adding to the love of map and grep, we can write a simple command-line parser.

my %opts = map { $_ => 1 } grep { /^-/ } @ARGV;

If we want, we can set each flag to it's index in @ARGV:

my %opts = map { $ARGV[$_] => $_ } grep { $ARGV[$_] =~ /^-/ } 0 .. $#ARGV;

That way, if a flag has an argument, we can get the argument like this:

if( defined( $opts{-e} ) ) {
  my $arg = $ARGV[ $opts{-e} ];
  # do -e stuff for $arg
}

Of course, some people will cry that we're reinventing the wheel and we should use getopt or some variant thereof, but honestly, this was a fairly easy wheel to reinvent. Plus, I don't like getopt.

If you don't like how long some of those lines are, you can always use intermediate variables or just convenient line breaks (hey, Python fanatics? You hear that? We can put one line of code across two lines and it still works!) to make it look better:

my %opts = map  { $ARGV[$_] => $_   }
           grep { $ARGV[$_] =~ /^-/ } 0 .. $#ARGV;

This file parsing mechanism is compact and easy to customize (skip blank lines, skip lines starting with X, etc..).

open(H_CONFIG, "< $file_name") or die("Error opening file: $file_name! ($!)");
while (<H_CONFIG>)
{
   chomp;         # remove the trailing newline
   next if $_ =~ /^\s*$/; # skip lines that are blank
   next if $_ =~ /^\s*#/; # skip lines starting with comments
   # do something with the line
}

I use this type of construct in diverse build situations - where I need to either pre or post process payload files (S-records, etc..) or C-files or gather directory information for a 'smart build'.


My favourite elegant Perl feature is that it uses different operators for numerical values and string values.

my $string = 1 . 2;
my $number = "1" + "2";
my $unambiguous = 1 . "2";

Compare this to other dynamic languages such as JavaScript, where "+" is used for concatenation and addition.

var string = "1" + "2";
var number = 1 + 2;
var ambiguous = 1 + "2";

Or to dynamic languages such as Python and Ruby that require type coercion between strings and numberical values.

string = "1" + "2"
number = 1 + 2
throws_exception = 1 + "2"

In my opinion Perl gets this so right and the other languages get it so wrong.


Poorer typists like me who get cramps hitting the shift key too often and have an almost irrational fear of using a semicolon started writing our Perl code in python formatted files. :)

e.g.

>>> k = 5
>>> reduce(lambda i,j: i*j, range(1,k+1),1)
120
>>> k = 0
>>> reduce(lambda i,j: i*j, range(1,k+1),1)
1

Need Your Help

What are the security effects of giving the IIS_IUSRS a Full Control permission?

asp.net asp.net-mvc security iis

What are the the security effects of giving the IIS_IUSRS a Full Control permission on the root folder of ASP.NET websites?

How to get current time from internet in android

android time

I am making an app in which I want to get the current time from internet.