... Using Perl
use strict;
my $thing = shift || 'world'; # I hope "0" is not an okay value :-)
my $required = shift or die "Nothing specified on the command line.\n";
my( $switch, $param );
$switch = shift;
if( $switch and $switch eq "-g" ) {
$param = shift || 'world';
} else {
$param = $switch || shift || 'meow';
$switch = undef if $switch;
}
print $switch ? 'Goodbye' : 'Hello', ", $param\n";
(then carry on...)
use Getopt::Long;
my $data = "file.dat";
my $length = 24;
my $verbose;
GetOptions("length=i" => \$length, # numeric
"file=s" => \$data, # string
"verbose" => \$verbose) # flag
or die("Error in command line arguments\n");
(from perldoc GetOpt::Long, first result of "perl parse command line" on DDG)
... I'm not saying the grass is always greener
"Perl 6 is a highly capable, feature-rich programming language
made for the coming hundred years."
(as you might've noticed, I didn't manage to include a "Multimethod" pun in the title)
Which means you're not forced to put types on things, but if you do, they'll be enforced
(statically - at BEGIN time - if possible)
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
# .IO converts the Str to a IO::Path
# says True if "path" *e*xists
# (.IO converts a string to a IO::Path)
say "path".IO.e;
for "path1", "path2", "path3" {
say $_.IO.e;
# in Perl 6, we can also use a bare dot to mean "$_":
say .IO.e; # <- ".IO" acts on $_
}
(manually checks some condition)
Thankfully...
# 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 use smart-match:
say "myfile.p6" ~~ File; # True
say "this file doesn't exist" ~~ File; # False
# smart-matches against the range
subset SmallPosInt of Int where 0..10;
say 0 ~~ SmallPosInt; # True
say 10 ~~ SmallPosInt; # True
say 100000 ~~ SmallPosInt; # False
# same as doing this directly:
say 3 ~~ 0..10; # True
subset Comment of Str where /^'#'/;
say "abc" ~~ Comment; # False
say "# abc" ~~ Comment; # True
# read a file...
my @lines = 'file.c'.IO.lines;
# and extract the comments!
my @comments = @lines.grep(Comment);
sub greet($name) {
# $name is interpolated inside double quotes
say "Hi $name!";
}
greet("people"); #=> Hi people!
# Perl 6 is a bit more permissive with identifiers:
sub greet-name($who-to-greet) {
# you can also use bracket to explicitly interpolate something
# (not needed here)
say "Hello {$who-to-greet}!";
}
greet-name("world"); #=> Hello world
# You can also add type constraints
sub welcome-back(Str $place) {
say "Welcome back $place!";
}
welcome-back("home"); #=> Welcome back home!
# welcome-back(3); # will fail
(If possible, the type check will fail at compile-time)
# You can ask the parameter to be coerced
# (here, from Int to Str)
sub say-count(Str(Int) $num) {
say $num ~~ Str;
}
# prints True:
# 5 has been converted to a Str
say-count(5);
# If the parentheses are empty, it'll convert any type to Str
# (by calling its .Str method)
sub as-string(Str() $n) {
$n eq "1 2 3 4 5";
}
as-string(1..5); # True as well
# You can also add by-name arguments
sub welcome(Str :$name) {
say "Named arguments are amazing, right, $name?";
}
# calling it:
welcome(name => "Perl People");
welcome(:name("Everybody"));
# this would fail:
#welcome("a string passed as a positional");
multi sub print-me(Int $x) {
say "$x is an Int";
}
multi sub print-me(Str $x) {
say "$x is a Str";
}
# Literals are also allowed
multi sub strize(0) { say "Zero" }
# the "sub" is implicit:
multi strize(1) { say "One" }
# the parameter can be anonymous:
multi strize($) { say "(??)" }
subset File of Str where .IO.e;
# use a type-constrained sub parameter
sub read-file(File $f) {
# Perl6 will check the file exists when you call read-file
say slurp $f;
}
subset Comment of Str where /^'#'/;
# No need to even have a sigil, the type is enough
multi sub is-comment(Comment) { True }
# (calling `is-comment` with a non-Str will still fail)
multi sub is-comment(Str) { False }
# equivalent to this form:
multi sub comments($x where /^'#'/, $y where /^'//') {
...
}
This is a valid Perl 6 program:
# (the star twigil is for variables like Perl5's `local`,
# which mean they are dynamically scoped)
my $name = @*ARGS[0];
# or
my ($name) = @*ARGS;
say "Hello, $name!";
But it's no improvement over what we had
(and it's pretty ugly/error-prone)
# 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>
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>
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
#| Return a guaranteed-to-be-random value.
sub random-value {
return 4; # chosen by fair dice roll
}
say &random-value.WHY;
#=> prints "Return a guaranteed-to-be-random value."
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
# again -- we can use literals!
multi MAIN('add', Int $x, Int $y) {
say $x + $y;
}
multi MAIN('div', Int $x, Int $y) {
say $x / $y;
}
multi 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>
Examples:
sub MAIN(:$role = "lord") {
say "Well met, my $role";
}
$ ./named.p6 --help
Usage:
named.p6 [--role=<Any>]
$ ./named.p6
Well met, my lord
$ ./named.p6 --role=sir
Well met, my sir
Examples:
sub MAIN(Str :$role = "lord") {
say "Well met, my $role";
}
$ ./named.p6 --help
Usage:
named.p6 [--role=<Str>]
$ ./named.p6
Well met, my lord
$ ./named.p6 --role=sir
Well met, my sir
Examples:
sub MAIN(Bool :$verbose) {
say "Turning on verbose mode..." if $verbose;
}
$ ./named.p6 --help
Usage:
named.p6 [--verbose]
$ ./named.p6
$ ./named.p6 --verbose
Turning on verbose mode...
subset File of Str where .IO.e;
subset NonFile of Str where !.IO.e;
multi sub MAIN(File $move-from, NonFile $move-to) {
rename $move-from, $move-to;
say "Moved!";
}
multi sub MAIN($, File $) {
say "The destination already exists";
}
multi sub MAIN(NonFile $, $) {
say "The source file doesn't exist";
}
$ ./advent.p6
Usage:
advent.p6 <move-from> <move-to>
advent.p6 <Any> (File)
advent.p6 (NonFile) <Any>
The fallback candidates are documented, which we don't want.
It also shows an ugly "Any" for the arguments
(default type for a sub parameter)
subset File of Str where .IO.e;
subset NonFile of Str where !.IO.e;
#| Move a file
multi sub MAIN(File $move-from, NonFile $move-to) {
rename $move-from, $move-to;
say "Moved!";
}
multi sub MAIN($, File $) is hidden-from-USAGE {
say "The destination already exists";
}
multi sub MAIN(NonFile $, $) is hidden-from-USAGE {
say "The source file doesn't exist";
}
$ ./advent.p6
Usage:
advent.p6 <move-from> <move-to> -- Move a file
Only the "good" candidate is displayed, with its documentation.
The two others aren't in --help, but still work.
With Perl 6, we gained the ability to declaratively create a CLI. Not because the language added a lot of special syntax or rules for that, but rather thanks to orthogonal features working together.