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
- 976