Perl 6 Idioms, and Idiomatic Perl 6

(Butterflies of the world, alight!)

Perl is a richly expressive language, with a warm and playful community. When someone crafts a succinct way to solve a common problem, the Perl community often adopts that solution's phrasing as a idiom. (Other-times, the community recoils in horror and proposes a less obtuse phrasing.)

Perl 6 adds many elements to the language, and not just keywords; there are metaoperators, clean exceptions, new contexts, better interpolation, frugal OO, and much more. Those additions were shaped by patterns of Perl 5 use (with an eye to future uses), so at least one should scratch some itch you have long ignored. The new bits aren't required, but we like the shiny, so be prepared for a slew of new idioms soon after Christmas.

Most Perl 6 idioms will not just be translated versions of familiar Perl 5 idioms; they will use new features where appropriate, and may seem unfamiliar at first. If you regularly use any of the Perl 5 idioms below, you can expect to grok its new form soon after you embrace Perl 6 itself.

Idiomatic Perl 6 should feel just as Perlish as Perl 5, once you get used to it. After all, it is all Perl.

Conventions

(Details, details)

Definitions (corrupted^W adapted for computer languages):

Idiom

A phrase expected to be used to express an idea.

Idiomatic

Expressed as the language's native users would state it.

In most examples below, the code is shown in four versions:

1. Non-idiomatic Perl 5,
2. then made idiomatic.
#
3. Perl 5 idiom, naively translated into Perl 6,
4. then made idiomatic.

Any versions past #4 are to show TMTOWTDI. Notice how the code gets clearer or more concise as it goes from 1 to 4.

Idiom ==> Word

(Movin' on up)

Some Perl 5 idioms were so useful and conceptually concise, that they became Perl 6 "words" in their own right. These words take the form of new built-in operators, functions, and methods.

 # Pick a random array element
 $z = $array[ int(rand scalar(@array)) ];
 $z = $array[ rand @array ];
 #
 $z = @array[ rand*@array ];
 $z = @array.pick;            


 # Loop over the keys (indexes) of an array
 for ( my $i=0; $i<@array; $i++ ) {...}
 for my $i ( 0 .. $#array ) {...}
 #
 for 0 .. @array.end -> $i {...}
 for @array.keys -> $i {...}
 
 
 # Whole number division
 ( ($x - ($x % 3) ) / 3 )
 int( $x / 3 )
 #
 Int( $x / 3 )
 $x div 3                  # Integer division op


 # Print the count of the elements of an array.
 say scalar @array;
 say 0+@array;
 #
 say 0+@array;      # Identical in Perl 6
 say +@array;       # + forces the new "numeric" context
 say @array.elems;  # .elems method is more explicit.


 # Do something every 5th time
 if ( ($x/5) == int($x/5) ) {...}
 if ( !($x % 5) ) {...}
 #
 if !($x % 5) {...}
 if $x %% 5 {...}           # %% means "is evenly divisible by"


 # Do something $n times, counting up to $n-1
 for ( $_=0; $_ < $n; $_++ ) {...}
 for ( 0 .. ($n-1) ) {...}
 #
 for 0 ..^ $n {...}
 for ^$n {...}                      # ^9 means 0 ..^ 9, or 0..8

Bare method calls are *always* methods on $_, eliminating Perl 5's confusion on which functions default to $_.

Other defaults have been tweaked to move from something-else-I-must-remember to obvious.

 # Split on whitespace
 @words = split /\s+/, $_;
 @words = split;          # Default is useful, but not intuitive
 #
 @words = .split(/\s+/);  # split() now has no default pattern
 @words = .words;         # The old default split is now a method


 # Split a string into individual characters.
 @chars = map { substr $word, $_, 1 } 0..length($word);
 @chars = split '', $word; # Split on nothingness
 #
 @chars = $word.split('');
 @chars = $word.comb;      # Default is to "keep everything"


 # Infinite loop
 for (;;) {...}    # Spoken with a 'C' accent
 while (1) {...}
 #
 while 1 {...}
 loop {...}        # No limit given, so endless by default

Some idioms that became words were already words in Perl 5, if you used the appropriate module.

 # Return the unique elements from a list, in original order
 my %s, @r; for @a { push @r, $_ if !$s{$_}; $s{$_}++; } return @r;
 my %s; return grep { !$s{$_}++ } @a;    # or List::MoreUtils::uniq
 #
 my %s; return grep { !%s{$_}++ }, @a;
 return @a.uniq;


 # Add up all list elements
 my $sum = 0; for my $num (@a) { $sum += $num }
 my $sum; $sum += $_ for @a;    # or List::Util::sum
 #
 my $sum = @a.reduce(*+*);
 my $sum = [+] @a;              # [op] applies op to entire list

Idiom ==> Idiom

(The song remains the same)

Some idioms remain the same, modulo required syntax changes.

 @alpha = 'A' .. 'Z';

 @a = qw{ able baker charlie };
 
 %meta = ( foo => 'bar', baz => 'quz' );
 
 @squares = map { $_ * $_ }, @a;
 
 @starts_with_number = grep { /^\d/ }, @a;

Didn't those all look familiar? Sure, parenthesis are no longer needed on list assignment, and qw{} now has a an angle-bracket shortcut, but those are optional, and don't really change the gist of the idioms. Add that comma after the map|grep block, and these Perl 5 idioms still stand strong in Perl 6.

Perl 5's "magic diamond" idiom still exists, just spelled more sanely (and less diamond-ly).

 # Process each line from STDIN or from command-line files.
 for my $file (@ARGV) { open FH, $file; while (<FH>) {...} }
 while (<>) {...}               # Null filehandle is magical
 #
 for @*ARGFILES.lines {...}
 for lines() {...}              # lines() defaults to $fh = $*ARGFILES

Idiom 5 ==> Idiom 6

(The more things change, the more they remain the same)

Some idioms have taken new form, when the idea behind the original idiom found better expression through new language elements.

 # Hash initialization to constant
 my %h;   for (@a) { $h{$_} = 1 }
 my %h = map { $_ => 1 } @a;
 #
 my %h = map { $_ => 1 }, @a;
 my %h = @a X=> 1;


 # Hash initialization for enumeration
 my %h;   for (0..$#a) { $h{ $a[$_] } = $_ }
 my $c;   my %h = map { $_ => ++$c } @a;
 #
 my $c;   my %h = map { $_ => ++$c }, @a;
 my %h = @a Z=> 1..*;
 my %h = @a.pairs».invert;  # if zero based

 # Hash initialization from parallel arrays
 my %h;   for (@a) { $h{$_} = shift @b }
 my %h;   @h{@a} = @b;
 #
 my %h;   %h{@a} = @b;
 my %h = @a Z=> @b;

 # Swap two variables
 my $temp = $x; $x = $y; $y = $temp;
 ( $x, $y ) = ( $y, $x );
 ( $x, $y ) =   $y, $x;
 #
 ( $x, $y ) .= reverse;   # .= makes reverse into a "mutating" method
 # Tastes great on array swaps, too!   @a[ $j, $k ] .= reverse;

 # Rotate array left by 1 element
 my $temp = shift @a; push @a, $temp;
 push @a, shift @a;
 #
 @a.push: @a.shift;
 @a .= rotate;

 # Create an object
 my $pet = new Dog;
 my $pet = Dog->new;
 #
 my $pet = Dog.new;
 my Dog $pet .= new;    # $pet *always* isa Dog; Compiler can optimize!

Combining transformation with selection was an advanced idiom in Perl 5. The new return values for if provide a bite-sized idiom.

 # Three copies of elements > 5
 @z = map { ($_) x 3 } grep { $_ > 5 } @y;    # map,grep
 @z = map { $_ > 5 ? ($_) x 3 : () } @y;      # map as grep
 #
 @z = map { $_ > 5 ?? ($_) xx 3 !! Nil }, @y;
 @z = @y.map: { $_ xx 3 if $_ > 5 };          # !if == Empty list
 @z = ($_ xx 3 if $_ > 5 for @y);             # List comprehension

That fifth form is a list comprehension, popular in functional languages, and in our closer cousin.

Sentence|Paragraph ==> Idiom

(The territory becomes the map)

Some larger code blocks can now be expressed so concisely that they become idioms in their own right.

 # Random integer between 3 and 7 inclusive
 do { $z = int rand 8 } until $z >= 3;
 $z = 3 + int rand 5;
 #
 $z = 3 + Int(5.rand);
 $z = (3..7).pick;

 # Count by 3 in an infinite loop
 for ( my $i = 1; ; $i++ ) { my $n = 3 * $i; ... }
 for ( my $n = 3; ; $n += 3 ) {...}
 #
 loop ( my $n = 3; ; $n += 3 ) {...}
 for 3, * + 3 ... * -> $n {...}      # `...` is the "sequence" operator
 for 3, 6, 9 ... * -> $n {...}       # `...` can infer from example list

 # Loop over a range, excluding the start and end points
 for my $i ( $start .. $limit ) { next if $i == $start or $i == $limit; ... }
 for my $i ( ($start+1) .. ($limit-1) ) {...}
 #
 for ($start+1) .. ($limit-1) -> $i {...}
 for $start ^..^ $limit -> $i {...}

Predictions

(Gaze into my crystal ball)

(Still writing this part)

Predictions for how idiomatic Perl 6 will be influenced by other design elements, after Christmas

Coders get a much earlier intro to exceptions, via open(), so they are more likely to design using exceptions!

More use of slicing, since single-element hash|array access is just a degenerate form of multiple-element access.

».methods for parallelism with set-at-a-time thinking

lazy lists

more mixing of procedural&OO&functional paradigms due to code blocks and OO being "cheaper"

DSL modules just to talk more sanely about your problem.