
It's like a group chat for Mastodon, Pleroma, Friendica, and the rest. Discuss software development in the Perl programming language.
It's like a group chat for Mastodon, Pleroma, Friendica, and the rest. Discuss software development in the Perl programming language.
Haven't touched #Perl in a while? Modern Perl might surprise you and I'm super-excited that my #Corinna #OOP proposal has been accepted. Here's a simple 2D point class in the new syntax: class Point { field ($x,$y) :param :reader; method invert() { ($x,$y) = ($y,$x) } } For serious OOP fans: it incl...
Friday, December 17, 2021, marked the thirty-fourth birthday of the Perl programming language, and coincidentally this year saw the release of version 5.34. There are plenty of Perl developers out there who haven’t kept up with recent (and not-so-recent) improvements to the language and its ecosystem, so I thought I might list a batch. (You may have seen some of these before in May’s post “Perl can do that now!”)
feature
pragmaPerl v5.10 was released in December 2007, and with it came feature
, a way of enabling new syntax without breaking backward compatibility. You can enable individual features by name (e.g., use feature qw(say fc);
for the say
and fc
keywords), or by using a feature bundle based on the Perl version that introduced them. For example, the following:
use feature ':5.34';
…gives you the equivalent of:
use feature qw(bareword_filehandles bitwise current_sub evalbytes fc indirect multidimensional postderef_qq say state switch unicode_eval unicode_strings);
Boy, that’s a mouthful. Feature bundles are good. The corresponding bundle also gets implicitly loaded if you specify a minimum required Perl version, e.g., with use v5.32;
. If you use v5.12;
or higher, strict
mode is enabled for free. So just say:
use v5.34;
And lastly, one-liners can use the -E
switch instead of -e
to enable all features for that version of Perl, so you can say the following on the command line:
perl -E 'say "Hello world!"'
Instead of:
perl -e 'print "Hello world!\n"'
Which is great when you’re trying to save some typing.
experimental
pragmaSometimes new Perl features need to be driven a couple of releases around the block before their behavior settles. Those experiments are documented in the perlexperiment page, and usually, you need both a use feature
(see above) and no warnings
statement to safely enable them. Or you can simply pass a list to use experimental
of the features you want, e.g.:
use experimental qw(isa postderef signatures);
warnings
categoriesMarch 2000 saw the release of Perl 5.6, and with it, the expansion of the -w
command-line switch to a system of fine-grained controls for warning against “dubious constructs” that can be turned on and off depending on the lexical scope. What started as 26 main and 20 subcategories has expanded into 31 main and 43 subcategories, including warnings for the aforementioned experimental features.
As the relevant Perl::Critic policy says, “Using warnings, and paying attention to what they say, is probably the single most effective way to improve the quality of your code.” If you must violate warnings (perhaps because you’re rehabilitating some legacy code), you can isolate such violations to a small scope and individual categories. Check out the strictures module on CPAN if you’d like to go further and make a safe subset of these categories fatal during development.
Not every new bit of Perl syntax is enabled with a feature
guard. For the rest, there’s E. Choroba’s Syntax::Construct module on CPAN. Rather than having to remember which version of Perl introduced what, Syntax::Construct lets you declare only what you use and provides a helpful error message if someone tries to run your code on an older unsupported version. Between it and the feature
pragma, you can prevent many head-scratching moments and give your users a chance to either upgrade or workaround.
autodie
Many of Perl’s built-in functions only return false on failure, requiring the developer to check every time whether a file can be open
ed or a system
command executed. The lexical autodie
pragma replaces them with versions that raise an exception with an object that can be interrogated for further details. No matter how many functions or methods deep a problem occurs, you can choose to catch it and respond appropriately. This leads us to…
try
/catch
exception handling and Feature::Compat::TryThis year’s Perl v5.34 release introduced experimental try
/catch
syntax for exception handling that should look more familiar to users of other languages while handling the issues surrounding using block eval
and testing of the special [email protected]
variable. If you need to remain compatible with older versions of Perl (back to v5.14), just use the Feature::Compat::Try module from CPAN to automatically select either v5.34’s native try
/catch
or a subset of the functionality provided by Syntax::Keyword::Try.
The abovementioned Syntax::Keyword::Try was made possible by the introduction of a pluggable keyword mechanism in 2010’s Perl v5.12. So was the Future::AsyncAwait asynchronous programming library and the Object::Pad testbed for new object-oriented Perl syntax. If you’re handy with C and Perl’s XS glue language, check out Paul “LeoNerd” Evans’ XS::Parse::Keyword module to get a leg up on developing your own syntax module.
package
s with versions and blocksPerl v5.12 also helped reduce clutter by enabling a package
namespace declaration to also include a version number, instead of requiring a separate our $VERSION = ...;
v5.14 further refined package
s to be specified in code blocks, so a namespace declaration can be the same as a lexical scope. Putting the two together gives you:
package Local::NewHotness v1.2.3 {
...
}
Instead of:
{
package Local::OldAndBusted;
use version 0.77; our $VERSION = version->declare("v1.2.3");
...
}
I know which I’d rather do. (Though you may want to also use Syntax::Construct qw(package-version package-block);
to help along with older installations as described above.)
//
defined-or operatorThis is an easy win from Perl v5.10:
defined $foo ? $foo : $bar # replace this
$foo // $bar # with this
And:
$foo = $bar unless defined $foo # replace this
$foo //= $bar # with this
Perfect for assigning defaults to variables.
state
variables only initialize onceSpeaking of variables, ever want one to keep its old value the next time a scope is entered, like in a sub
? Declare it with state
instead of my
. Before Perl v5.10, you needed to use a closure instead.
say
Perl v5.10’s bumper crop of enhancements also included the say
function, which handles the common use case of print
ing a string or list of strings with a newline. It’s less noise in your code and saves you four characters. What’s not to love?
...
The ...
ellipsis statement (colloquially “yada-yada”) gives you an easy placeholder for yet-to-be-implemented code. It parses OK but will throw an exception if executed. Hopefully, your test coverage (or at least static analysis) will catch it before your users do.
each
, keys
, and values
The each
, keys
, and values
functions have always been able to operate on hashes. Perl v5.12 and above make them work on arrays, too. The latter two are mainly for consistency, but you can use each
to iterate over an array’s indices and values at the same time:
while (my ($index, $value) = each @array) {
...
}
This can be problematic in non-trivial loops, but I’ve found it helpful in quick scripts and one-liners.
delete local
hash (and array) entriesEver needed to delete
an entry from a hash (e.g, an environment variable from %ENV
or a signal handler from %SIG
) just inside a block? Perl v5.12 lets you do that with delete local
.
Jumping forward to 2014’s Perl v5.20, the new %foo{'bar', 'baz'}
syntax enables you to slice a subset of a hash with its keys and values intact. Very helpful for cherry-picking or aggregating many hashes into one. For example:
my %args = (
verbose => 1,
name => 'Mark',
extra => 'pizza',
);
# don't frob the pizza
$my_object->frob( %args{ qw(verbose name) };
Not to be left out, you can also slice arrays in the same way, in this case returning indices and values:
my @letters = 'a' .. 'z';
my @subset_kv = %letters[16, 5, 18, 12];
# @subset_kv is now (16, 'p', 5, 'e', 18, 'r', 12, 'l')
Perl v5.20 introduced and v5.24 de-experimentalized a more readable postfix dereferencing syntax for navigating nested data structures. Instead of using {
braces}
or smooshing sigils to the left of identifiers, you can use a postfixed sigil-and-star:
push @$array_ref, 1, 2, 3; # noisy
push @{$array_ref}, 1, 2, 3; # a little easier
push $array_ref->@*, 1, 2, 3; # read from left to right
So much of web development is slinging around and picking apart complicated data structures via JSON, so I welcome anything like this to reduce the cognitive load.
when
as a statement modifierStarting in Perl v5.12, you can use the experimental switch feature’s when
keyword as a postfix modifier. For example:
for ($foo) {
$a = 1 when /^abc/;
$a = 42 when /^dna/;
...
}
But I don’t recommend when
, given
, or given
’s smartmatch operations as they were retconned as experiments in 2013’s Perl v5.18 and have remained so due to their tricky behavior. I wrote about some alternatives using stable syntax back in February.
use parent
Sometimes in older object-oriented Perl code, you’ll see use base
as a pragma to establish inheritance from another class. Older still is the direct manipulation of the package’s special @ISA
array. In most cases, both should be avoided in favor of use parent
, which was added to core in Perl v5.10.1.
Mind you, if you’re following the Perl object-oriented tutorial’s advice and have selected an OO system from CPAN, use its subclassing mechanism if it has one. Moose, Moo, and Class::Accessor’s “antlers” mode all provide an extends
function; Object::Pad provides an :isa
attribute on its class
keyword.
isa
operatorAs an alternative to the isa()
method provided to all Perl objects, Perl v5.32 introduced the experimental isa
infix operator:
$my_object->isa('Local::MyClass')
# or
$my_object isa Local::MyClass
The latter can take either a bareword class name or string expression, but more importantly, it’s safer as it also returns false if the left argument is undefined or isn’t a bless
ed object reference. The older isa()
method will throw an exception in the former case and might return true if called as a class method when $my_object
is actually a string of a class name that’s the same as or inherits from isa()
’s argument.
Introduced in Perl v5.18 and de-experimentalized in 2017’s Perl v5.26, you can now precede sub declarations with my
, state
, or our
. One use of the first two is truly private functions and methods, as described in this 2018 Dave Jacoby blog and as part of Neil Bowers’ 2014 survey of private function techniques.
I’ve written and presented extensively about signatures and alternatives over the past year, so I won’t repeat that here. I’ll just add that the Perl 5 Porters development mailing list has been making a concerted effort over the past month to hash out the remaining issues towards rendering this feature non-experimental. The popular Mojolicious real-time web framework also provides a shortcut for enabling signatures and uses them extensively in examples.
<<~
Perl has had shell-style “here-document” syntax for embedding multi-line strings of quoted text for a long time. Starting with Perl v5.26, you can precede the delimiting string with a ~
character and Perl will both allow the ending delimiter to be indented as well as strip indentation from the embedded text. This allows for much more readable embedded code such as runs of HTML and SQL. For example:
if ($do_query) {
my $rows_deleted = $dbh->do(<<~'END_SQL', undef, 42);
DELETE FROM table
WHERE status = ?
END_SQL
say "$rows_deleted rows were deleted.";
}
When I learned math in school, my teachers and textbooks would often describe multiple comparisons and inequalities as a single expression. Unfortunately, when it came time to learn programming every computer language I saw required them to be broken up with a series of and
(or &&
) operators. With Perl v5.32, this is no more:
if ( $x < $y && $y <= $z ) { ... } # old way
if ( $x < $y <= $z ) { ... } # new way
It’s more concise, less noisy, and more like what regular math looks like.
Perl’s expressive regular expression matching and text-processing prowess are legendary, although overuse and poor use of readability enhancements often turn people away from them (and Perl in general). We often use regexps for extracting data from a matched pattern. For example:
if ( /Time: (..):(..):(..)/ ) { # parse out values
say "$1 hours, $2 minutes, $3 seconds";
}
Named capture groups, introduced in Perl v5.10, make both the pattern more obvious and retrieval of its data less cryptic:
if ( /Time: (?<hours>..):(?<minutes>..):(?<seconds>..)/ ) {
say "$+{hours} hours, $+{minutes} minutes, $+{seconds} seconds";
}
The /x
regular expression modifier already enables better readability by telling the parser to ignore most whitespace, allowing you to break up complicated patterns into spaced-out groups and multiple lines with code comments. With Perl v5.26 you can specify /xx
to also ignore spaces and tabs inside [
bracketed]
character classes, turning this:
/[d-eg-i3-7]/
/[[email protected]"#$%^&*()=?<>']/
…into this:
/ [d-e g-i 3-7]/xx
/[ ! @ " # $ % ^ & * () = ? <> ' ]/xx
re
pragmaBeginning with Perl v5.14, writing use re '/xms';
(or any combination of regular expression modifier flags) will turn on those flags until the end of that lexical scope, saving you the trouble of remembering them every time.
s///r
and tr///r
The s///
substitution and tr///
transliteration operators typically change their input directly, often in conjunction with the =~
binding operator:
s/foo/bar/; # changes the first foo to bar in $_
$baz =~ s/foo/bar/; # the same but in $baz
But what if you want to leave the original untouched, such as when processing an array of strings with a map
? With Perl v5.14 and above, add the /r
flag, which makes the substitution on a copy and returns the result:
my @changed = map { s/foo/bar/r } @original;
fc
for better string comparisonsUnicode and character encoding in general are complicated beasts. Perl has handled Unicode since v5.6 and has kept pace with fixes and support for updated standards in the intervening decades. If you need to test if two strings are equal regardless of case, use the fc
function introduced in Perl v5.16.
<<>>
The <>
null filehandle or “diamond operator” is often used in while
loops to process input per line coming either from standard input (e.g., piped from another program) or from a list of files on the command line. Unfortunately, it uses a form of Perl’s open
function that interprets special characters such as pipes (|
) that would allow it to insecurely run external commands. Using the <<>>
“double diamond” operator introduced in Perl v5.22 forces open
to treat all command-line arguments as file names only. For older Perls, the perlop documentation recommends the ARGV::readonly CPAN module.
@INC
Perl v5.26 removed the ability for all programs to load modules by default from the current directory, closing a security vulnerability originally identified and fixed as CVE-2016–1238 in previous versions’ included scripts. If your code relied on this unsafe behavior, the v5.26 release notes include steps on how to adapt.
To bootstrap access to CPAN on the web in the possible absence of external tools like curl
or wget
, Perl v5.14 began including the HTTP::Tiny module. You can also use it in your programs if you need a simple web client with no dependencies.
Forked and refactored from the venerable Test::Builder (the basis for the Test::More library that many are familiar with), Test2 was included in the core module library beginning with Perl v5.26. I’ve experimented recently with using the Test2::Suite CPAN library instead of Test::More and it looks pretty good. I’m also intrigued by Test2::Harness’ support for threading, forking, and preloading modules to reduce test run times.
This last item may not be included when you install Perl, but it’s where I turn for a collection of well-regarded CPAN modules for accomplishing a wide variety of common tasks spanning from asynchronous programming to XML. Use it as a starting point or interactively select the mix of libraries appropriate to your project.
And there you have it: a selection of 34 features, enhancements, and improvements for the first 34 years of Perl. What’s your favorite? Did I miss anything? Let me know in the comments.
Look, I get it. You don’t like the Perl programming language or have otherwise disregarded it as “dead.” (Or perhaps you haven’t, in which case please check out my other blog posts!) It has weird noisy syntax, mixing regular expressions, sigils on variable names, various braces and brackets for data structures, and a menagerie of cryptic special variables. It’s old: 34 years in December, with a history of (sometimes amateur) developers that have used and abused that syntax to ship code of questionable quality. Maybe you grudgingly accept its utility but think it should die gracefully, maintained only to run legacy applications.
But you know what? Perl’s still going. It’s had a steady cadence of yearly releases for the past decade, introducing new features and fencing in bad behavior while maintaining an admirable level of backward compatibility. Yes, there was a too-long adventure developing what started as Perl 6, but that language now has its own identity as Raku and even has facilities for mixing Perl with its native code or vice versa.
And then there’s CPAN, the Comprehensive Perl Archive Network: a continually-updated collection of over 200,000 open-source modules written by over 14,000 authors, the best of which are well-tested and ‑documented (applying peer pressure to those that fall short), presented through a search engine and front-end built by scores of contributors. Through CPAN you can find distributions for things like:
All of this is available through a mature installation toolchain that doesn’t break from month to month.
Finally and most importantly, there’s the global Perl community. The COVID-19 pandemic has put a damper on the hundreds of global Perl Mongers groups’ meetups, but that hasn’t stopped the yearly Perl and Raku Conference from meeting virtually. (In the past there have also been yearly European and Asian conferences, occasional forays into South America and Russia, as well as hackathons and workshops worldwide.) There are IRC servers and channels for chat, mailing lists galore, blogs (yes, apart from this one), and a quirky social network that predates Facebook and Twitter.
So no, Perl isn’t dead or even dying, but if you don’t like it and favor something newer, that’s OK! Technologies can coexist on their own merits and advocates of one don’t have to beat down their contemporaries to be successful. Perl happens to be battle-tested (to borrow a term from my friend Curtis “Ovid” Poe), it runs large parts of the Web (speaking from direct and ongoing experience in the hosting business here), and it’s still evolving to meet the needs of its users.
Twitter recently recommended a tweet to me (all hail the algorithm) touting what the author viewed as the “top 5 web development stacks.”
JavaScript/Node.js options dominated the four-letter acronyms as expected, but the fifth one surprised me: LAMP, the combination of the Linux operating system, Apache web server, MySQL relational database, and Perl, PHP, or Python programming languages. A quick web search for similar lists yielded similar results. Clearly, this meme (in the Dawkins sense) has outlasted its popularization by tech publisher O’Reilly in the 2000s.
Originally coined in 1998 during the “dot-com” bubble, I had thought that the term “LAMP” had faded with developers in the intervening decades with the rise of language-specific web frameworks for:
Certainly on the Perl side (with which I’m most familiar), the community has long since recommended the use of a framework built on the PSGI specification, deprecating 1990s-era CGI scripts and the mod_perl Apache extension. Although general-purpose web servers like Apache or Nginx may be part of an overall system, they’re typically used as proxies or load balancers for Perl-specific servers either provided by the framework or a third-party module.
Granted, PHP still relies on web server-specific modules, APIs, or variations of the FastCGI protocol for interfacing with a web server. And Python web applications typically make use of its WSGI protocol either as a web server extension or, like the Perl examples above, as a proxied standalone server. But all of these are deployment details and do little to describe how developers implement and extend a web application’s structure.
Note how the various four-letter JavaScript stacks (e.g., MERN, MEVN, MEAN, PERN) differentiate themselves mostly by frontend framework (e.g., Angular, React, Vue.js) and maybe by the (relational or NoSQL) database (e.g., MongoDB, MySQL, PostgreSQL). All however seem standardized on the Node.js runtime and Express backend web framework, which could, in theory, be replaced with non-JavaScript options like the more mature LAMP-associated languages and frameworks. (Or if you prefer languages that don’t start with “P”, there’s C#, Go, Java, Ruby, etc.)
My point is that “LAMP” as the name of a web development stack has outlived its usefulness. It’s at once too specific (about operating system and web server details that are often abstracted away for developers) and too broad (covering three separate programming languages and not the frameworks they favor). It also leaves out other non-JavaScript back-end languages and their associated frameworks.
The question is: what can replace it? I’d propose “NoJS” as reminiscent of “NoSQL,” but that inaccurately excludes JavaScript from its necessary role in the front-end. “NJSB” doesn’t exactly roll off the tongue, either, and still has the same ambiguity problem as “LAMP.”
How about pithy sort-of-acronyms patterned like database-frontend-backend? Here are some Perl examples:
Ultimately it comes down to community and industry adoption. If you’re involved with back-end web development, please let me know in the comments if you agree or disagree that “LAMP” is still a useful term, and if not, what should replace it.