My recommendation against explicit scalar
in Embracing Idioms gathered some attention. "It's clearer to read," said some people. "It's more obvious what I mean," said others.
The best advice I can give any fledgling Perl 5 programmer is to read anything Mark Jason Dominus writes, especially his Higher Order Perl. That book may be daunting, so for now start with Ovid's journal entry about synthetic classes. He quotes MJD, who responds himself:
I realized after a couple of years that architects have ... "structural" and "functional" elements.
One of the characteristics of novice code is that it focuses on structural elements. When someone says "You write Perl like a C programmer", it often means that you're doing too much work in your loops:
for (my $i = 0; $i < @elems; ++$i)
{
my $elem = $elems[$i];
say "$elem";
}
Compare that to the Perlish use of iteration:
for my $elem (@elems)
{
say $elem;
}
... or the postfix iteration, implicit topic form:
say for @elems;
Sometimes you can even get away with the version which exploits list context:
say @elems;
(Though note that the current output record separator has a greater effect on the functional equivalence of the final version.
In this case, the structure of the iteration is less important than the function of printing every element of the array. While for people learning Perl, the multiple seemingly-equivalent options seem overwhelming, daunting with fragmentation possibilities, experienced Perl programmers can choose the form which best expresses the intended function while minimizing synthetic code.
Perhaps it's clearer to explain why map
and grep
are important.
Perl 5's map
iterates over a list and produces a list. That's
it. You can produce a list of the first ten square numbers with the code:
my @squares = map { $_ * $_ } 1 .. 10;
The equivalent iteration version might be:
my @squares;
for my $i ( 1 .. 10 )
{
push @squares, $i * $i;
}
If you're not familiar with map
, the first version may seem
inscrutable and the second comforting. Yet compare the amount of structural
code in the second example. The iteration is explicit. Creating list elements
is explicit. Creating the resulting list is explicit. Extending the resulting
list is explicit.
Similarly, grep
removes synthetic code:
my @primes = grep { is_prime( $_ ) } @maybe_primes;
... versus:
my @primes;
for my $maybe_prime (@maybe_primes)
{
next unless is_prime( $maybe_prime );
push @primes, $maybe_prime;
}
Perl 6 takes this further by introducing a concept of metaoperators (see Perl 6 Synopsis 3), which allow the use of any existing operator -- built in or user-defined -- to reduce or distribute over a list (in parallel, even) or more.
This is not to say that it's bad or wrong to use structural code, especially as a novice. Learning to program takes work. Learning syntax and design and the interaction of symbols in symbolic computation is complex. Even so, one of your goals should be to reach the point where you can evaluate syntax and idioms and choose the ones which best clarify the intent and function of your code, rather than the ones which emphasize the structure of how you accomplish that goal.
The more functional tools 'map' and 'grep' were sort of foreign for me at first, but once one understands what they do, they are incredible useful. I reach for map all the time, not least to peer into data structures with a 'print map { $_ . "\n" } @stuff'. Using grep with a subroutine is an idiom I should use more often as well.