circus theme party

Last week’s arti­cle got a great response on Hacker News, and this par­tic­u­lar com­ment caught my eye:

I think this is the real point about Perl code read­abil­i­ty: it gives you enough flex­i­bil­i­ty to do things how­ev­er you like, and as a result many pro­gram­mers are faced with a mir­ror that reflects their own bad prac­tices back at them.

orev, Hacker News

This is why Damian Conway’s Perl Best Practices (2005) is one of my favorite books and perlcritic, the code ana­lyz­er is one of my favorite tools. (Though the for­mer could do with an update and the lat­ter includes poli­cies that con­tra­dict Conway.) Point perlcritic at your code, maybe add some oth­er poli­cies that agree with your house style, and grad­u­al­ly ratch­et up the sever­i­ty lev­el from gen­tle” to bru­tal.” All kinds of bad juju will come to light, from waste­ful­ly using grep to hav­ing too many sub­rou­tine argu­ments to catch­ing pri­vate vari­able use from oth­er pack­ages. perlcritic offers a use­ful base­line of con­duct and you can always cus­tomize its con­fig­u­ra­tion to your own tastes.

The oth­er con­for­mance tool in a Perl devel­op­er’s belt is perltidy, and it too has a Conway-​compatible con­fig­u­ra­tion as well as its default Perl Style Guide set­tings. I’ve found that more than any­thing else, perltidy helps set­tle argu­ments both between devel­op­ers and between their code in help­ing to avoid exces­sive merge conflicts.

But apart from extra tools, Perl the lan­guage itself can be bent and even bro­ken to suit just about any­one’s agen­da. Those used to more bondage-​and-​discipline lan­guages (hi, Java!) might feel revul­sion at the lengths to which this has some­times been tak­en, but per the quote above this is less an indict­ment of the lan­guage and more of its less method­i­cal pro­gram­mers.

Some of this behav­ior can be reha­bil­i­tat­ed with perlcritic and perltidy, but what about oth­er sins attrib­uted to Perl? Here are a few peren­ni­al favorites”:

Objects and Object-​Oriented Programming

Perl has a min­i­mal­ist object sys­tem based on earlier-​available lan­guage con­cepts like data struc­tures (often hash­es, which it has in com­mon with JavaScript), pack­ages, and sub­rou­tines. Since Perl 5’s release in 1994 much ver­bose OO code has been writ­ten using these tools.

The good news is that since 2007 we’ve had a sophis­ti­cat­ed metaobject-​protocol-​based lay­er on top of them called Moose, since 2010 a light­weight but forward-​compatible sys­tem called Moo, and a cou­ple of even tinier options as described in the Perl OO Tutorial. Waiting in the wings is Corinna, an effort to bring next-​generation object capa­bil­i­ties into the Perl core itself, and Object::Pad, a test­bed for some of the ideas in Corinna that you can use today in cur­rent code. (Really, please try it — the author needs feed­back!)

All this is to say that 99% of the time you nev­er need trou­ble your­self with bless, con­struc­tors, or writ­ing acces­sors for class or object attrib­ut­es. Smarter peo­ple than me have done the work for you, and you might even find a con­cept or three that you wish oth­er lan­guages had.

Contexts

There are two major ones: list and scalar. Another way to think of it is plur­al” vs. sin­gu­lar” in English, which is hope­ful­ly a thing you’re famil­iar with as you’re read­ing this blog.

Some func­tions in Perl act dif­fer­ent­ly depend­ing on whether the expect­ed return val­ue is a list or a scalar, and a func­tion will pro­vide a list or scalar con­text to its argu­ments. Mostly these act just as you would expect or would like them to, and you can find out how a func­tion behaves by read­ing its doc­u­men­ta­tion. Your own func­tions can behave like this too, but there’s usu­al­ly no need as both scalars and lists are auto­mat­i­cal­ly inter­pret­ed into lists.” Again, Perl’s DWIMmery at work.

Subroutine and Method Arguments

I’ve already writ­ten about this. Twice. And pre­sent­ed about it. Twice. The short ver­sion: Perl has sig­na­tures, but they’ve been con­sid­ered exper­i­men­tal for a while. In the mean­time, there are alter­na­tives on CPAN. You can even have type con­straints if you want.


I’ll leave you with this: Over the past month, Neil Bowers of the Perl Steering Council has been col­lect­ing quirks like these from Perl devel­op­ers. The PSC is review­ing this col­lec­tion for poten­tial doc­u­men­ta­tion fix­es, bug fix­es, fur­ther dis­cus­sion, etc. I would­n’t expect to see any fun­da­men­tal changes to the lan­guage out of this effort, but it’s a good sign that poten­tial­ly con­fus­ing fea­tures are being addressed. 

I’ll be repris­ing my pre­sen­ta­tion on Perl sub­rou­tine sig­na­tures and type val­i­da­tion for the Boston Perl Mongers on Tuesday, March 9 at 7 PM EST. Visit their wiki for details; they’ll be post­ing the Jitsi URL short­ly before the meet­ing. There’s also a Meetup page.

A few weeks ago I wrote an arti­cle cov­er­ing six ways to do sub­rou­tine sig­na­tures and type con­straints in Perl. Some of the feed­back I got indi­cat­ed that there were more options to con­sid­er, so for the pre­sen­ta­tion I gave at Houston Perl Mongers I quick­ly sum­ma­rized at the end sev­en more mod­ules from CPAN that offer these fea­tures. Still more feed­back showed that I had missed five more mod­ules, so at the risk of becom­ing the sig­na­tures guy” here they are.

Class::ParamParser (updated)

First released in 2000 as part of a larg­er project, this may be the first general-​purpose para­me­ter pars­er to appear on CPAN. Although the doc­u­men­ta­tion exam­ples use it as a par­ent class, you should fol­low the sug­ges­tion fur­ther down and use its two meth­ods params_to_hash and params_to_array direct­ly from the class. Both take the same list of argu­ments, and only dif­fer on whether they return a hash ref­er­ence con­tain­ing named para­me­ters or an array ref­er­ence con­tain­ing posi­tion­al parameters.

There are options for slurp­ing extra passed val­ues into anoth­er hash buck­et and low­er­cas­ing named para­me­ters, and it will auto­mat­i­cal­ly chop off any lead­ing hyphens in your para­me­ter names. You can also alias mul­ti­ple named para­me­ters to the same result­ing argu­ment. The mod­ule does­n’t pro­vide any facil­i­ty for type val­i­da­tion, though.

Method::ParamValidator

This adds the inter­est­ing wrin­kle of being con­fig­urable with an exter­nal JSON file; oth­er­wise, it seems you have to call var­i­ous meth­ods on a val­ida­tor object to add para­me­ters (which it calls fields) or meth­ods to val­i­date. It does­n’t seem to sup­port slurp­ing extra val­ues nor posi­tion­al para­me­ters, but its type val­i­da­tion lets you use arbi­trary code ref­er­ences. The doc­u­men­ta­tion could use a bit of work with more exam­ples of real-​world usage rather than test script extracts. The author says it’s just a pro­to­type,” though.

Mojolicious::Validator

The Mojolicious real-​time web frame­work has a built-​in val­ida­tor, but it’s geared towards val­i­dat­ing HTTP para­me­ters rather than sub­rou­tine argu­ments. There’s also a MojoX::Validator wrap­per for Input::Validator that per­forms much the same task. You could press either into ser­vice as sub­rou­tine val­ida­tors if you’re already using Mojolicious, but they aren’t struc­tured that way.

Return::Type

This isn’t for input para­me­ters to sub­rou­tines; rather, it pro­vides an attribute that spec­i­fies what type your sub­rou­tines return. As such, it’s com­pli­men­ta­ry to most of the oth­er tech­niques cov­ered in this series. It works with Type::Tiny, MooseX::Types, or MouseX::Types, and because Perl func­tions can tell whether they’re in scalar or list con­text, you can spec­i­fy dif­fer­ent type con­straints for each.

There are sev­er­al oth­er mod­ules with sim­i­lar func­tion­al­i­ty, but these are out­side the scope of this article.

routines

Based on Function::Parameters (cov­ered pre­vi­ous­ly), this prag­ma adds sup­port for a reg­istry for stor­ing Type::Tiny type libraries. It does­n’t appear to offer any oth­er sig­na­ture fea­tures, though. Feel free to cor­rect me in the comments.

As I said in my slide deck, maybe we’re tak­ing TMTOWTDI too far? It’s nice to be spoiled for options, though. I still rec­om­mend Type::Tinys Type::Params for its per­for­mance and flex­i­bil­i­ty, or Params::ValidationCompiler if you’re using type libraries from Specio or Moose. Some use­ful side-​by-​side bench­marks would be nice; maybe I’ll save that for anoth­er arti­cle and fur­ther risk being labeled the sig­na­tures guy.”

I’ll be pre­sent­ing Better Perl: Subroutine Signatures and Type Validation” (based on my ear­li­er blog post) for Houston Perl Mongers on Thursday, February 11 at 6:00 PM Central US time.

Check here to see what time that is in your local time zone, then go here at that time to open the Zoom meet­ing ID 920 069 702. You’ll need a pass­word; run this Perl code to get that:

print +(0b1000100).((3<<2)*10).(010)

Did you know that you could increase the read­abil­i­ty and reli­a­bil­i­ty of your Perl code with one fea­ture? I’m talk­ing about sub­rou­tine sig­na­tures: the abil­i­ty to declare what argu­ments, and in some cas­es what types of argu­ments, your func­tions and meth­ods take.

Most Perl pro­gram­mers know about the @_ vari­able (or @ARG if you use English). When a sub­rou­tine is called, @_ con­tains the para­me­ters passed. It’s an array (thus the @ sig­il) and can be treat­ed as such; it’s even the default argu­ment for pop and shift. Here’s an example:

use v5.10;
use strict;
use warnings;

sub foo {
    my $parameter = shift;
    say "You passed me $parameter";
}

Or for mul­ti­ple parameters:

use v5.10;
use strict;
use warnings;

sub foo {
    my ($parameter1, $parameter2) = @_;
    say "You passed me $parameter1 and $parameter2";
}

(What’s that use v5.10; doing there? It enables all fea­tures that were intro­duced in Perl 5.10, such as the say func­tion. We’ll assume you type it in from now on to reduce clutter.)

We can do bet­ter, though. Perl 5.20 (released in 2014; why haven’t you upgrad­ed?) intro­duced the exper­i­men­tal signatures fea­ture, which as described above, allows para­me­ters to be intro­duced right when you declare the sub­rou­tine. It looks like this:

use experimental 'signatures';

sub foo ($parameter1, $parameter2 = 1, @rest) {
    say "You passed me $parameter1 and $parameter2";
    say "And these:";
    say for @rest;
}

You can even set defaults for option­al para­me­ters, as seen above with the = sign, or slurp up remain­ing para­me­ters into an array, like the @rest array above. For more help­ful uses of this fea­ture, con­sult the perl­sub man­u­al page.

We can do bet­ter still. The Comprehensive Perl Archive Network (CPAN) con­tains sev­er­al mod­ules that both enable sig­na­tures, as well as val­i­date para­me­ters are of a cer­tain type or for­mat. (Yes, Perl can have types!) Let’s take a tour of some of them.

Params::Validate

This mod­ule adds two new func­tions, validate() and validate_pos(). validate() intro­duces named para­me­ters, which make your code more read­able by describ­ing what para­me­ters are being called at the time you call them. It looks like this:

use Params::Validate;

say foo(parameter1 => 'hello',  parameter2 => 'world');

sub foo {
    my %p = validate(@_, {
        parameter1 => 1, # mandatory
        parameter2 => 0, # optional
    } );
    return $p->{parameter1}, $p->{parameter2};
}

If all you want to do is val­i­date un-​named (posi­tion­al) para­me­ters, use validate_pos():

use Params::Validate;

say foo('hello', 'world');

sub foo {
    my @p = validate_pos(@_, 1, 0);
    return @p;
}

Params::Validate also has fair­ly deep sup­port for type val­i­da­tion, enabling you to val­i­date para­me­ters against sim­ple types, method inter­faces (also known as duck typ­ing”), mem­ber­ship in a class, reg­u­lar expres­sion match­es, and arbi­trary code call­backs. As always, con­sult the doc­u­men­ta­tion for the nitty-​gritty details.

MooseX::Params::Validate

MooseX::Params::Validate adds type val­i­da­tion via the Moose object-​oriented frame­work’s type sys­tem, mean­ing that any­thing that can be defined as a Moose type can be used to val­i­date the para­me­ters passed to your func­tions or meth­ods. It adds the validated_hash(), validated_list(), and pos_validated_list() func­tions, and looks like this:

package Foo;

use Moose;
use MooseX::Params::Validate;

say __PACKAGE__->foo(parameter1 => 'Mouse');
say __PACKAGE__->bar(parameter1 => 'Mice');
say __PACKAGE__->baz('Men', 42);

sub foo {
    my ($self, %params) = validated_hash(
        \@_,
        parameter1 => { isa => 'Str', default => 'Moose' },
    );
    return $params{parameter1};
}

sub bar {
    my ($self, $param1) = validated_pos(
        \@_,
        parameter1 => { isa => 'Str', default => 'Moose' },
    );
    return $param1;
}

sub baz {
    my ($self, $foo, $bar) = pos_validated_list(
        \@_,
        { isa => 'Str' },
        { isa => 'Int' },
    );
    return $foo, $bar;
}

Note that the first para­me­ter passed to each func­tion is a ref­er­ence to the @_ array, denot­ed by a backslash.

MooseX::Params::Validate has sev­er­al more things you can spec­i­fy when list­ing para­me­ters, includ­ing roles, coer­cions, and depen­den­cies. The doc­u­men­ta­tion for the mod­ule has all the details. We use this mod­ule at work a lot, and even use it with­out Moose when val­i­dat­ing para­me­ters passed to test functions.

Function::Parameters

For a dif­fer­ent take on sub­rou­tine sig­na­tures, you can use the Function::Parameters mod­ule. Rather than pro­vid­ing helper func­tions, it defines two new Perl key­words, fun and method. It looks like this:

use Function::Parameters;

say foo('hello', 'world');
say bar(param1 => 'hello');

fun foo($param1, $param2) {
    return $param1, $param2;
}

fun bar(:$param1, :$param2 = 42) {
    return $param1, $param2;
}

The colons in the bar() func­tion above indi­cate that the para­me­ters are named, and need to be spec­i­fied by name when the func­tion is called, using the => oper­a­tor as if you were spec­i­fy­ing a hash.

In addi­tion to defaults and the posi­tion­al and named para­me­ters demon­strat­ed above, Function::Parameters sup­ports type con­straints (via Type::Tiny) and Moo or Moose method mod­i­fiers. (If you don’t know what those are, the Moose and Class::Method::Modifiers doc­u­men­ta­tion are helpful.)

I’m not a fan of mod­ules that add new syn­tax for com­mon tasks like sub­rou­tines and meth­ods, if only because there’s an extra effort in updat­ing tool­ings like syn­tax high­lighters and Perl::Critic code analy­sis. Still, this may appeal to you, espe­cial­ly if you’re com­ing from oth­er lan­guages that have sim­i­lar syntax.

Type::Params

Speaking of Type::Tiny, it includes its own para­me­ter val­i­da­tion library called Type::Params. I think I would favor this for new work, as it’s com­pat­i­ble with both Moo and Moose but does­n’t require them.

Type::Params has a num­ber of func­tions, none of which are pro­vid­ed by default, so you’ll have to import them explic­it­ly when use ing the mod­ule. It also intro­duces a sep­a­rate step for com­pil­ing your val­i­da­tion spec­i­fi­ca­tion to speed up per­for­mance. It looks like this:

use Types::Standard qw(Str Int);
use Type::Params qw(compile compile_named);

say foo('hello', 42);
say bar(param1 => 'hello');

sub foo {
    state $check = compile(Str, Int);
    my ($param1, $param2) = $check->(@_);

    return $param1, $param2;
}

sub bar {
    state $check = compile_named(
        param1 => Str,
        param2 => Int, {optional => 1},
    );
    my $params_ref = $check->(@_);

    return $params_ref->{param1}, $params_ref->{param2};
}

The fea­tures of Type::Tiny and its bun­dled mod­ules are pret­ty vast, so I sug­gest once again that you con­sult the doc­u­men­ta­tion on how to use it.

Params::ValidationCompiler

At the top of the doc­u­men­ta­tion to Params::Validate, you’ll notice that the author rec­om­mends instead his Params::ValidationCompiler mod­ule for faster per­for­mance, using a com­pi­la­tion step much like Type::Params. It pro­vides two func­tions for you to import, validation_for() and source_for(). We’ll con­cen­trate on the for­mer since the lat­ter is main­ly use­ful for debugging.

It looks like this:

use Types::Standard qw(Int Str);
use Params::ValidationCompiler 'validation_for';

my $validator = validation_for(
    params => {
        param1 => {
            type    => Str,
            default => 'Perl is cool',
        },
        param2 => {
            type     => Int,
            optional => 1,
        },
);

say foo(param1 => 'hello');

sub foo {
    my %params = $validator->(@_);
    return @params{'param1', 'param2'};
}

As you can see, it sup­ports type con­straints, defaults, and option­al val­ues. It can also put extra argu­ments in a list (it calls this fea­ture slur­py”), and can even return gen­er­at­ed objects to make it eas­i­er to catch typos (since a typoed hash key just gen­er­ates that key rather than return­ing an error). There’s a bit more to this mod­ule, so please read the doc­u­men­ta­tion to exam­ine all its features.

Conclusion

One of Perl’s mot­tos is there’s more than one way to do it,” and you’re wel­come to choose what­ev­er method you need to enable sig­na­tures and type val­i­da­tion. Just remem­ber to be con­sis­tent and have good rea­sons for your choic­es, since the over­all goal is to improve your code’s reli­a­bil­i­ty and read­abil­i­ty. And be sure to share your favorite tech­niques with oth­ers, so they too can devel­op bet­ter software.

This arti­cle is also pub­lished on dev.to.