One criticism of opponents of Perl is that it is a "write-only" language meaning that once the code is written, it is extremely difficult to maintain because it is difficult to understand upon re-examination. As with many criticisms, this should be aimed at those undisciplined developers who are writing the code, and not their tool of choice.
Having said that, I think it is also fair to say that Perl makes it very easy to write difficult-to-decipher code. This is the doubleedged sword which is the shorthand Perl gives us to be very expressive in a small amount of space. A negative application of this is obfuscated Perl (where the author intentionally makes his code difficult to read), while a more positive application is the craft of creating Perl "oneliners" (trying to include a great deal of functionality in a single line of code). A oneliner can be a powerful weapon in the arsenal of a system administrator.
In this talk:
* We'll look at a line of code in a subroutine that is in desperate need of readability changes
* We'll make the code more readable by introducing:
* appropriate whitespace
* different ways of writing the same thing, for example: $array[$#array] vs. $array[-1]
* useful names for variables, versus $index, $j $k $l, etc
* breaking up one line of code into multiple lines
* exploring further improvements through Perl::Critic and by extension Perl::Tidy
This talk will be beginner-friendly.
3. Who am I?
● Web Engineer at OmniTI
– Full stack tech consulting
– Remote database management and consulting
– Large Scale / Mission Critical
– We're Hiring!
– omniti.com
6. Why This Talk?
● Is Perl a "write-only" language? No!
● ...but it can be.
7. Why This Talk?
● Is Perl a "write-only" language? No!
● ...but it can be.
● It's our responsibility to keep code:
8. Why This Talk?
● Is Perl a "write-only" language? No!
● ...but it can be.
● It's our responsibility to keep code:
– Easy to read
9. Why This Talk?
● Is Perl a "write-only" language? No!
● ...but it can be.
● It's our responsibility to keep code:
– Easy to read, so it will be:
10. Why This Talk?
● Is Perl a "write-only" language? No!
● ...but it can be.
● It's our responsibility to keep code:
– Easy to read, so it will be:
– Easy to maintain
12. Purpose of listify
Take a list, for example:
List # 1, with 10 items
one two three four five six seven eight nine ten
Give me several lists back, each containing no more than N items,
for example, 4:
List #1 with 4 items: one two three four
List #2 with 4 items: five six seven eight
List #3 with 2 items: nine ten
13. Prepare to call listify
listify( @list, 4 );
Result:
@array = (
[ 'one', 'two', 'three', 'four' ],
[ 'five', 'six', 'seven', 'eight' ],
[ 'nine', 'ten' ],
);
my @list = qw( one two three four five six seven eight nine ten );
14. Call to listify
my @list = qw( one two three four five six seven eight nine ten );
Result:
@array = (
[ 'one', 'two', 'three', 'four' ],
[ 'five', 'six', 'seven', 'eight' ],
[ 'nine', 'ten' ],
);
listify( @list, 4 );
15. Result of listify
my @list = qw( one two three four five six seven eight nine ten );
listify( @list, 4 );
Result:
@array = (
[ 'one', 'two', 'three', 'four' ],
[ 'five', 'six', 'seven', 'eight' ],
[ 'nine', 'ten' ],
);
16. Declare function listify
sub listify {
my ($aref,$cc) = @_;
if( ref $aref eq 'ARRAY' && $cc > 0 ) {
my $j;
for(my $i=0; $i<=$#$aref; $i+=$cc) {
push @$j, [@$aref[$i..$i+$cc-1]];
}
$#{$j->[$#{$j}]}=$#$aref%$cc;
@$aref = @$j;
return 1;
}
return;
}
sub listify {
}
17. Get Parameters
sub listify {
my ($aref,$cc) = @_;
if( ref $aref eq 'ARRAY' && $cc > 0 ) {
my $j;
for(my $i=0; $i<=$#$aref; $i+=$cc) {
push @$j, [@$aref[$i..$i+$cc-1]];
}
$#{$j->[$#{$j}]}=$#$aref%$cc;
@$aref = @$j;
return 1;
}
return;
}
my ($aref,$cc) = @_;
25. Clever (but not easy to read) Line
$#{$j->[$#{$j}]}=$#$aref%$cc;
26. Purpose of Clever Line
The purpose of the line is to truncate the final array to the number of remaining
elements so we don't end up with this:
@array = (
[ 'one', 'two', 'three', 'four' ],
[ 'five', 'six', 'seven', 'eight' ],
[ 'nine', 'ten', undef, undef ],
);
31. Improvement #5: Split Another Line
my $final_aref = $result_array[ -1 ];
my $elements_in_final = $#$in_aref % $elements_per_array;
$#$final_aref = $elements_in_final;
32. Improvement #6: More Whitespace
my $final_aref = $result_array[ -1 ];
my $elements_in_final = $#$in_aref % $elements_per_array;
$#$final_aref = $elements_in_final;
33. Improvement #7: A comment!
my $final_aref = $result_array[ -1 ];
my $elements_in_final = $#$in_aref % $elements_per_array;
# Truncate final array
$#$final_aref = $elements_in_final;
34. Easier to Read Now?
Original:
$#{$j->[$#{$j}]}=$#$aref%$cc;
Revised:
my $final_aref = $result_array[ -1 ];
my $elements_in_final = $#$in_aref % $elements_per_array;
# Truncate final array
$#$final_aref = $elements_in_final;
35. Apply Principles to Entire Function
sub listify {
my ( $in_aref, $elements_per_array ) = @_;
return if (
ref $in_aref ne 'ARRAY' or
$elements_per_array <= 0
);
my @result_array;
for( my $i = 0; $i <= $#$in_aref; $i += $elements_per_array ) {
push @result_array, [
@$in_aref[ $i..$i + $elements_per_array - 1 ]
];
}
# Continued on next slide
36. Apply Principles to Entire Function
# Continued from previous slide
my $final_aref = $result_array[ -1 ];
my $elements_in_final = $#$in_aref % $elements_per_array;
# Truncate final array
$#$final_aref = $elements_in_final;
@$in_aref = @result_array;
}
38. perlcritic Result
$ perlcritic -1 listify.pl
Code not contained in explicit package at line 1, column 1. Violates
encapsulation. (Severity: 4)
Code before strictures are enabled at line 1, column 1. See page
429 of PBP. (Severity: 5)
Subroutine "listify" does not end with "return" at line 1, column 1.
See page 197 of PBP. (Severity: 4)
Code before warnings are enabled at line 1, column 1. See page 431 of
PBP. (Severity: 4)
No package-scoped "$VERSION" variable found at line 1, column 1. See
page 404 of PBP. (Severity: 2)
C-style "for" loop used at line 10, column 7. See page 100 of PBP.
(Severity: 2)
Double-sigil dereference at line 10, column 26. See page 228 of PBP.
(Severity: 2)
(more double-sigil warnings follow)
39. Critical Fixes
Code not contained in explicit package at line 1, column 1. Violates
encapsulation. (Severity: 4)
Code before strictures are enabled at line 1, column 1. See page 429
of PBP. (Severity: 5)
Code before warnings are enabled at line 1, column 1. See page 431
of PBP. (Severity: 4)
sub listify {
my ( $in_aref, $elements_per_array ) = @_;
# --- cut ---
#!/usr/bin/perl
use strict;
use warnings;
40. Explicit Return
Subroutine "listify" does not end with "return" at line 2,
column 1. See page 197 of PBP. (Severity: 4)
# Truncate final array
$#$final_aref = $elements_in_final;
@$in_aref = @result_array;
}
return;
41. $VERSION
No package-scoped "$VERSION" variable found at line 1,
column 1. See page 404 of PBP. (Severity: 2)
#!/usr/bin/perl
use strict;
use warnings;
sub listify {
my ( $in_aref, $elements_per_array ) = @_;
# --- cut ---
our $VERSION = '1.19';
43. C-style "for" Loop
C-style "for" loop used at line 11, column 7. See page 100
of PBP. (Severity: 2)
● “What?” No C-style “for” loops
44. C-style "for" Loop
C-style "for" loop used at line 11, column 7. See page 100
of PBP. (Severity: 2)
● “What?” No C-style “for” loops
● “Why?” More difficult to read and maintain
45. C-style "for" Loop
C-style "for" loop used at line 11, column 7. See page 100
of PBP. (Severity: 2)
● “What?” No C-style “for” loops
● “Why?” More difficult to read and maintain
● “I disagree.” That's OK. Modify Perl::Critic settings!
46. C-style "for" Loop
C-style "for" loop used at line 11, column 7. See page 100
of PBP. (Severity: 2)
● “What?” No C-style “for” loops
● “Why?” More difficult to read and maintain
● “I disagree.” That's OK. Modify Perl::Critic settings!
But today, I'm going to fix all the defaults
47. Change to “while” loop
C-style "for" loop used at line 11, column 7. See page 100 of
PBP. (Severity: 2)
Old:
for( my $i = 0; $i <= $#$in_aref; $i += $elements_per_array ) {
# (loop contents)
}
New:
my $i = 0;
while($i <= $#$in_aref) {
# (loop contents)
$i += $elements_per_array;
}
49. JAPH: The Do-Not Example
sub q{ord($_[0])}sub qq{chr($_[0])}(@q=(j,q,q,,q,s,,u,o,[$;=@q,$"=sub{for(@q){
s/(.)/&qq(&q($1)-1)/e}},$_=q,*534`!./4(%2`0%2{`(!#+%2,,tr;{;,;,s/(.)/&q($1)>95?
&qq(&q($1)-64):&qq(&q($ 1)+64)/eg,% q=(q,qq,,sub{eval$ _[0]},q,q,,1)])),&{$ "},
$q{qq}->(qq.$;->[$q=$q{q}]${$;}[++$q]$;->[$[]${$;}[-1+$)-$ )+$#q]$;->[$#q-$q].)
50. JAPH: The Do-Not Example
sub q{ord($_[0])}sub qq{chr($_[0])}(@q=(j,q,q,,q,s,,u,o,[$;=@q,$"=sub{for(@q){
s/(.)/&qq(&q($1)-1)/e}},$_=q,*534`!./4(%2`0%2{`(!#+%2,,tr;{;,;,s/(.)/&q($1)>95?
&qq(&q($1)-64):&qq(&q($ 1)+64)/eg,% q=(q,qq,,sub{eval$ _[0]},q,q,,1)])),&{$ "},
$q{qq}->(qq.$;->[$q=$q{q}]${$;}[++$q]$;->[$[]${$;}[-1+$)-$ )+$#q]$;->[$#q-$q].)
● Ugly variables $" $; $ " and even $_, @_
51. JAPH: The Do-Not Example
sub q{ord($_[0])}sub qq{chr($_[0])}(@q=(j,q,q,,q,s,,u,o,[$;=@q,$"=sub{for(@q){
s/(.)/&qq(&q($1)-1)/e}},$_=q,*534`!./4(%2`0%2{`(!#+%2,,tr;{;,;,s/(.)/&q($1)>95?
&qq(&q($1)-64):&qq(&q($ 1)+64)/eg,% q=(q,qq,,sub{eval$ _[0]},q,q,,1)])),&{$ "},
$q{qq}->(qq.$;->[$q=$q{q}]${$;}[++$q]$;->[$[]${$;}[-1+$)-$ )+$#q]$;->[$#q-$q].)
● Ugly variables $" $; $ " and even $_, @_
– use English;
52. JAPH: The Do-Not Example
sub q{ord($_[0])}sub qq{chr($_[0])}(@q=(j,q,q,,q,s,,u,o,[$;=@q,$"=sub{for(@q){
s/(.)/&qq(&q($1)-1)/e}},$_=q,*534`!./4(%2`0%2{`(!#+%2,,tr;{;,;,s/(.)/&q($1)>95?
&qq(&q($1)-64):&qq(&q($ 1)+64)/eg,% q=(q,qq,,sub{eval$ _[0]},q,q,,1)])),&{$ "},
$q{qq}->(qq.$;->[$q=$q{q}]${$;}[++$q]$;->[$[]${$;}[-1+$)-$ )+$#q]$;->[$#q-$q].)
● Ugly variables $" $; $ " and even $_, @_
– use English;
● 74% non-alpha/space
53. JAPH: The Do-Not Example
sub q{ord($_[0])}sub qq{chr($_[0])}(@q=(j,q,q,,q,s,,u,o,[$;=@q,$"=sub{for(@q){
s/(.)/&qq(&q($1)-1)/e}},$_=q,*534`!./4(%2`0%2{`(!#+%2,,tr;{;,;,s/(.)/&q($1)>95?
&qq(&q($1)-64):&qq(&q($ 1)+64)/eg,% q=(q,qq,,sub{eval$ _[0]},q,q,,1)])),&{$ "},
$q{qq}->(qq.$;->[$q=$q{q}]${$;}[++$q]$;->[$[]${$;}[-1+$)-$ )+$#q]$;->[$#q-$q].)
● Ugly variables $" $; $ " and even $_, @_
– use English;
● 74% non-alpha/space
– More non-alpha characters, more difficult to read
57. Other Considerations
● Conformity can be good
● How to implement good change?
● The “next guy” might be YOU.
● Comment to preempt disputes or misunderstandings
58. Best Book on this Topic
Perl Best Practices by Damian Conway
C-style &quot;for&quot; loop used at line 11, column 7. See page 100 of PBP. (Severity: 2)
“What?” Damian recommends against using C-style “for” loops”
“Why?” His main argument is that C-style “for” loops are more difficult to read and therefore more difficult to maintain
“I disagree.” That&apos;s OK and in fact Perl::Critic allows for a lot of customizations to alter or remove those policies you don&apos;t agree with
BUT, I&apos;m going to fix all the default suggestions for the purpose of this presentation
C-style &quot;for&quot; loop used at line 11, column 7. See page 100 of PBP. (Severity: 2)
“What?” Damian recommends against using C-style “for” loops”
“Why?” His main argument is that C-style “for” loops are more difficult to read and therefore more difficult to maintain
“I disagree.” That&apos;s OK and in fact Perl::Critic allows for a lot of customizations to alter or remove those policies you don&apos;t agree with
BUT, I&apos;m going to fix all the default suggestions for the purpose of this presentation
C-style &quot;for&quot; loop used at line 11, column 7. See page 100 of PBP. (Severity: 2)
“What?” Damian recommends against using C-style “for” loops”
“Why?” His main argument is that C-style “for” loops are more difficult to read and therefore more difficult to maintain
“I disagree.” That&apos;s OK and in fact Perl::Critic allows for a lot of customizations to alter or remove those policies you don&apos;t agree with
BUT, I&apos;m going to fix all the default suggestions for the purpose of this presentation
C-style &quot;for&quot; loop used at line 11, column 7. See page 100 of PBP. (Severity: 2)
“What?” Damian recommends against using C-style “for” loops”
“Why?” His main argument is that C-style “for” loops are more difficult to read and therefore more difficult to maintain
“I disagree.” That&apos;s OK and in fact Perl::Critic allows for a lot of customizations to alter or remove those policies you don&apos;t agree with
BUT, I&apos;m going to fix all the default suggestions for the purpose of this presentation
C-style &quot;for&quot; loop used at line 11, column 7. See page 100 of PBP. (Severity: 2)
“What?” Damian recommends against using C-style “for” loops”
“Why?” His main argument is that C-style “for” loops are more difficult to read and therefore more difficult to maintain
“I disagree.” That&apos;s OK and in fact Perl::Critic allows for a lot of customizations to alter or remove those policies you don&apos;t agree with
BUT, I&apos;m going to fix all the default suggestions for the purpose of this presentation
$&quot;, $;, $ &quot; (ignored space - crazy! Whitespace thus used to my advantage) even $_, @_
use English;
My obfuscated code is 74% non-alpha/space, and that isn&apos;t considering the abuse of alpha conventions like q qq s tr etc
The higher percentage of non-alpha characters, the more difficult to read
$&quot;, $;, $ &quot; (ignored space - crazy! Whitespace thus used to my advantage) even $_, @_
use English;
My obfuscated code is 74% non-alpha/space, and that isn&apos;t considering the abuse of alpha conventions like q qq s tr etc
The higher percentage of non-alpha characters, the more difficult to read
$&quot;, $;, $ &quot; (ignored space - crazy! Whitespace thus used to my advantage) even $_, @_
use English;
My obfuscated code is 74% non-alpha/space, and that isn&apos;t considering the abuse of alpha conventions like q qq s tr etc
The higher percentage of non-alpha characters, the more difficult to read
$&quot;, $;, $ &quot; (ignored space - crazy! Whitespace thus used to my advantage) even $_, @_
use English;
My obfuscated code is 74% non-alpha/space, and that isn&apos;t considering the abuse of alpha conventions like q qq s tr etc
The higher percentage of non-alpha characters, the more difficult to read
$&quot;, $;, $ &quot; (ignored space - crazy! Whitespace thus used to my advantage) even $_, @_
use English;
My obfuscated code is 74% non-alpha/space, and that isn&apos;t considering the abuse of alpha conventions like q qq s tr etc
The higher percentage of non-alpha characters, the more difficult to read
Conformity can be good: code like everyone else has in the apps you inherit or join. Unless it&apos;s horrible, then start a consistent style.
Then how to implement good change? One idea: implement across the entire code base, a little at a time. For example, get &quot;use strict&quot; working everywhere first.
Make it easy on the &quot;next guy&quot;. The next guy might be YOU.
Comment to pre-empt disputes or misunderstandings that will break production.
Conformity can be good: code like everyone else has in the apps you inherit or join. Unless it&apos;s horrible, then start a consistent style.
Then how to implement good change? One idea: implement across the entire code base, a little at a time. For example, get &quot;use strict&quot; working everywhere first.
Make it easy on the &quot;next guy&quot;. The next guy might be YOU.
Comment to pre-empt disputes or misunderstandings that will break production.
Conformity can be good: code like everyone else has in the apps you inherit or join. Unless it&apos;s horrible, then start a consistent style.
Then how to implement good change? One idea: implement across the entire code base, a little at a time. For example, get &quot;use strict&quot; working everywhere first.
Make it easy on the &quot;next guy&quot;. The next guy might be YOU.
Comment to pre-empt disputes or misunderstandings that will break production.
Conformity can be good: code like everyone else has in the apps you inherit or join. Unless it&apos;s horrible, then start a consistent style.
Then how to implement good change? One idea: implement across the entire code base, a little at a time. For example, get &quot;use strict&quot; working everywhere first.
Make it easy on the &quot;next guy&quot;. The next guy might be YOU.
Comment to pre-empt disputes or misunderstandings that will break production.