-
-
Save vendethiel/32057dc3750fc670937f to your computer and use it in GitHub Desktop.
The cool subset of MAIN.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
In the Unix environment, many scripts take arguments and options from the command line. With Perl 6 it's very easy to accept those. At least, that's what <a href="http://perl6advent.wordpress.com/2010/12/02/day-2-interacting-with-the-command-line-with-main-subs/">Our 2010 advent post on MAIN says</a>. | |
But today, we're interested in going further, and we're going to try to gain more expressiveness from it. | |
The first Perl 6 feature we're going to look at here is subsets. Subsets have a very broad range of applications, but they're even better when used in conjunction with MAIN. | |
Subsets are a declarative way to specify conditions on your type. If you wanted to translate "A QuiteSmallInt is an Int with a value lesser than 10", you can write | |
<pre> | |
subset QuiteSmallInt where * < 10; | |
</pre> | |
In case you don't remember from <a href="http://perl6advent.wordpress.com/2010/12/12/day-12-%E2%80%93-smart-matching/">our 2010 advent post about smart matching</a> or <a href="http://perl6advent.wordpress.com/2011/12/24/day-24-subs-are-always-better-in-multi-ples/">our 2011 advent post about multi-method dispatch</a>, here's a quick reminder of how you can use subsets with smart matching, multi-method dispatch or typed variables. | |
<pre> | |
say 9 ~~ QuiteSmallInt; # True | |
say 11 ~~ QuiteSmallInt; # False | |
multi sub small-enough(QuiteSmallInt) { say "Yes!" } | |
multi sub small-enough($) { say "No, too big."; } | |
small-enough(9); # Prints "Yes!" | |
small-enough(11); # Prints "Not quite enough." | |
# You can also use it to type variables | |
my QuiteSmallInt $n = 9; # Works! | |
my QuiteSmallInt $n = 11; # Errors out with "Type check failed in assignment to '$n'; ..." | |
</pre> | |
Alright; now that we got this out of the way, what does this buy us? | |
Well, as we just demonstrated, we can dispatch to different subroutines using those "where" conditions. | |
That goes for MAIN as well. | |
Say we wanted to take a filepath as the first argument of a MAIN. We'll also write two "companions" that'll give a descriptive error message in case we pass bad arguments. | |
<pre> | |
# a File is a string containing an existing filename | |
subset File of Str where *.IO.e; # .IO returns an IO::Path object, and .e is for "exists" | |
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"; | |
} | |
</pre> | |
And now, if we try to use it (after saving it as "main.p6")... | |
<pre> | |
$ touch file1.txt | |
$ perl6 ./main.p6 file1.txt destination.txt | |
Moved! | |
$ perl6 ./main.p6 non-existant.p6 non-existant.txt | |
The source file doesn't exist! | |
$ perl6 ./main.p6 destination.txt destination.txt | |
The destination already exists | |
</pre> | |
Alright, looks good. There's a little catch, however: the --help will display 3 usages, when there's only one. | |
<pre> | |
$ perl6 ./main.p6 --help | |
Usage: | |
tmp.p6 <move-from> <move-to> | |
tmp.p6 <Any> (File) | |
tmp.p6 (NonFile) <Any> | |
</pre> | |
Whoops! We need to find a way to hide them (if you're wondering, the "<Any>" is because the arguments both are unnamed and untyped). | |
Luckily enough, Perl6 provides a <a href="http://perl6advent.wordpress.com/2011/12/04/traits-meta-data-with-character/">trait</a> for that: hidden_from_USAGE, added by moritz++ for this advent post (USAGE is the --help message). | |
Let's add those to our MAINs. | |
<pre> | |
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"; | |
} | |
</pre> | |
And now, the USAGE is correct: | |
<pre> | |
$ perl6 ./main.p6 --help | |
Usage: | |
tmp.p6 <move-from> <move-to> | |
</pre> | |
Okay, now, we just need to add a description. | |
The second Perl6 feature we're going to use is Pod. | |
Pod is <a href="http://design.perl6.org/S26.html">an easy-to-use markup language</a>, which you can embed to document your code. | |
<pre> | |
#= Rename a file | |
multi sub MAIN(File $move-from, NonFile $move-to) { | |
rename $move-from, $move-to; | |
say "Moved!"; | |
} | |
# You can write it in multiple lines, though in USAGE they'll be collapsed | |
# (joined by a space) | |
#={Rename | |
a | |
file} | |
multi sub MAIN(File $move-from, NonFile $move-to) { | |
rename $move-from, $move-to; | |
say "Moved!"; | |
} | |
</pre> | |
Both versions above will print the same --help: | |
<pre> | |
$ perl6 ./main.p6 --help | |
Usage: | |
tmp.p6 <move-from> <move-to> -- Rename a file | |
</pre> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment