Sane scripting with Perl 6

Perl

"Perl is a glue language

for the shell"

Not your father's Perl

Perl 6 is a completely different language from Perl 5

... I'll try to convince you

Perl 6

 

"Perl 6 is a highly capable, feature-rich programming language
made for the coming hundred years.
"

 

Perl 6

 

Like a giant melting pot of (good) ideas.

Writing a script

 

We'll use:

  • Subsets
    (like Ada's subtypes)
  • Multimethods
    (like Common Lisp/Clojure's)
  • MAIN
    (not quite like C!)

Subsets

Perl 6 is gradually typed

Which means you're not forced to put types on things, but if you do, they'll be enforced

 


 my Str $text = "I'm a string!"; # A Str
 my Str @elems = "I'm", "a", "list", "of", "strings"; # Array of Str
 my Int %hash{Str} = a => 1, b => 2; # hash of Str to Int

Perl 6 allows us to create subtypes

They're a stricter version of their parent, with a checked precondition


 # Syntax:
 # subset $New-name of Type where Condition
 #  (default Type is Any)
 subset File of Str where .IO.e;
 # a "File" is a "Str" that exists

 # now, we can match on the type of the object:
 say "myfile.p6" ~~ File; # True
 say "incorrect filename" ~~ File; # False

 subset Comment of Str where /^'#'/;

 say "abc" ~~ Comment; # False

 say "# abc" ~~ Comment; # True

 # read a file...
 my @lines = 'file.php'.IO.lines;

 # and extract the comments!
 my @comments = @lines.grep(Comment);

Subroutines & multimethods

Perl 6 has featureful subroutine signatures


 # :$ denotes a named argument
 sub greet($name, :$greet = "Hi") {
   say "Hi $name!";
 }
 greet("people"); #=> Hi people!
 greet("people", greet => "Hello"); #=> Hello people!

 # you can also specify types in a signature
 sub add(Int $a, Int $b) { 
   return $a + $b;
 }
 add(3, "a string"); # Fails at *compile time*

 sub add-coercing(Int(Str) $a, Int() $b) {
   # $a can only be coerced from Str to Int
   # $b can be coerced from anything that has a .Int method
   return $a + $b;
 }

Multimethods

(or in our case, multi-subs)

How a basic
multi-subroutine looks like


 multi sub print-me(Int $x) {
   say "$x is an Int";
 }
 multi sub print-me(Str $x) {
   say "$x is a Str";
 }

 # You can also match on a literal
 multi sub strize(0) { say "Zero" }
 multi sub strize(1) { say "One" }
 # the parameter needn't be named:
 multi sub strize($) { say "(some other number)" }

These features work
great together!


 subset File of Str where .IO.e;

 # use a type-constrained sub parameter
 # Perl6 will check the file exists when you call read-file
 sub read-file(File $f) {
   ...;
 }

 # which is equivalent to an explicit `where`:
 sub read-file(Str $f where $f ~~ File) {
   ...;
 }

 subset Comment of Str where /^'#'/;

 # if we find a comment, discard it
 multi sub handle-line(Comment)   { } # do nothing
 # otherwise, print the line
 multi sub handle-line(Str $line) { say $line }

 # run handle-line on every line of the file
 read-file("my-file.text").lines.map(&handle-line);

Writing the script:

Our entry point

MAIN

Perl 6's built-in command-line parser


 # if you have a sub named MAIN declared,
 #  Perl6 will call it for you (also generate a --help message).
 sub MAIN($x, $y) {
   say "the result is {$x + $y}";
 }

Examples:

$ ./demo.p6 --help
Usage:
  demo.p6 <x> <y> 

$ ./demo.p6 3 4
the result is 7

$ ./demo.p6 "invalid arg"
Usage:
  demo.p6 <x> <y> 

Combining features

Using all these to write our script

  • Types
  • Multis
  • Named arguments
  • ...

Type-checking MAIN

Examples:


 sub MAIN(Int $x, Int $y) {
   say "the result is {$x + $y}";
 }
$ ./demo.p6 --help # still the same
Usage:
  demo.p6 <x> <y> 

$ ./demo.p6 3 4 # still the same
the result is 7

$ ./demo.p6 3 "some string here"
Usage:
  demo.p6 <x> <y> 

A Multi-MAIN


 multi sub MAIN(Int $x, Int $y) {
   say "the result is {$x + $y}";
 }

 # Use coercing to allow passing Int as Str
 #  for either param
 multi sub MAIN(Str() $x, Str() $y) {
   say "the result is $x$y";
 }

$ ./demo.p6
Usage:
  demo.p6 <x> <y> 
  demo.p6 <x> <y>

$ ./demo.p6 3 4
the result is 7

$ ./demo.p6 "a" "b"
the result is ab

# this would fail without the coercing:
$ ./demo.p6 3 "foo"
the result is 3foo

We need a better --help

The usage message doesn't give us a way to differentiate both versions.

Runtime-introspectable documentation


 #| Return a (totally) random value.
 sub random-value {
   return 4; # chosen by a fair dice roll
 } 

 say &random-value.WHY;
 #=> prints "Return a (totally) random value."

Documenting MAIN

Examples:


 #| Adds two numbers
 sub MAIN(Int $x, Int $y) { say $x + $y; }
$ ./demo.p6 --help # still the same
Usage:
  ./demo.p6 <x> <y> -- Adds two numbers

$ ./demo.p6 3 4 # still the same
the result is 7

$ ./demo.p6 3 "some string here"
Usage:
  ./demo.p6 <x> <y> -- Adds two numbers

Getting more of multi MAIN


 # using literal pattern matching
 multi sub MAIN('add', Int $x, Int $y)  {
   say $x + $y;
 }
 multi sub MAIN('div', Int $x, Int $y)  {
   say $x / $y;
 }
 multi sub MAIN('mult', Int $x, Int $y) {
   say $x * $y;
 }
$ ./calc.p6
Usage:
  calc.p6 add <x> <y> 
  calc.p6 div <x> <y> 
  calc.p6 mult <x> <y>

$ ./calc.p6 add 3 4
7

$ ./calc.p6 div 3 4
0.75

$ ./calc.p6 camel 3 4
Usage:
  calc.p6 add <x> <y> 
  calc.p6 div <x> <y> 
  calc.p6 mult <x> <y> 

Using typed named args

Examples:


 sub MAIN(Str :$role = "lord", Bool $:dry-run) {
   say "Well met, my $role" unless $dry-run;
 }

$ ./named.p6 --help
Usage:
  named.p6 [--role=<Str>] [--dry-run]

$ ./named.p6 
Well met, my lord

$ ./named.p6 --role=sir
Well met, my sir

$ ./named.p6 --role=sir --dry-run

Conclusion

This is only a small part of Perl 6.
Perl 6 has many different features:

  • Grammars: composable RegExps
  • A meta-object protocol
  • Very nice Unicode handling
  • Good concurrency story
  • Macros (still a WIP)
  • User-defined operators & meta-operators
  • ... Other things you might not need.

 

Using these features help you grow your scripts!

That's all!

I hope this sparked a bit of interest

Why you should learn Perl 6 next

By Vendethiel

Why you should learn Perl 6 next

  • 855