From 819f2163f6f0be26f2a711332e726a886c14e246 Mon Sep 17 00:00:00 2001 From: Patrick Spek Date: Sun, 6 May 2018 12:53:50 +0200 Subject: [PATCH] Add article to show off the MAIN sub --- _tutorials/perl6-showing-off-main.adoc | 258 +++++++++++++++++++++++++ 1 file changed, 258 insertions(+) create mode 100644 _tutorials/perl6-showing-off-main.adoc diff --git a/_tutorials/perl6-showing-off-main.adoc b/_tutorials/perl6-showing-off-main.adoc new file mode 100644 index 0000000..1667c92 --- /dev/null +++ b/_tutorials/perl6-showing-off-main.adoc @@ -0,0 +1,258 @@ +--- +date: 2018-05-05 15:13:40 +tags: Tutorial Perl6 MAIN +description: > + In this tutorial, I will explain about `USAGE`, a default subroutine that + shows how to use an application. What it actually displays is dependent on + the signature of `MAIN`. Additionally, some `Pod 6` will come in to play + this time around. +--- += Perl 6 - Showing off MAIN +:toc: preamble + +In this tutorial, I will explain about `USAGE`, a default subroutine that shows +how to use an application. What it actually displays is dependent on the +signature of `MAIN`. Additionally, some `Pod 6` will come in to play this time +around. + +If you don't have Perl 6 installed yet, do not worry. There's +link:/tutorials/perl6-setting-up-a-raspberry-perl#Installation[another tutorial +explaining this]. + +== MAIN +https://docs.perl6.org/language/functions#index-entry-MAIN[`MAIN`] is a special +sub in Perl 6. If a `MAIN` is defined in your program, this will be called when +your program is ran. If you don't have one, the script will be ran from top to +bottom. `MAIN` offers some nice benefits for you as the programmer, though, and +I would always recommend you to use it if your script is to be ran by a user. + +For starters, the `MAIN` sub can be easily recognized as the point of entry for +other developers coming from other languages, such as `C`, `Java` or `Go`. This +may not seem important to you now, but other people do generally like it if +they can understand what they're running, or at least would be able to learn +from it. + +Additionally, you can have multiple `MAIN` definitions using `multi`. This will +easily allow you to handle different inputs in their own, separate block. This +can help a great deal when trying to keep a code base clean. + +[source,sh] +---- +touch foo.p6 +echo 'multi sub MAIN($input) { say "You said $input!" }' >> foo.p6 +echo 'multi sub MAIN() { say "You said nothing :(" }' >> foo.p6 +perl6 foo.p6 # You said nothing :( +perl6 foo.p6 "repeat me" # You said repeat me! +---- + +My personal favorite benefit of the `MAIN` sub, is the default `USAGE` that you +get with it for free. After all, few people actively enjoy having to document +their application for the end user. And you'd have to maintain it too, in case +anything changes. But this is where Perl 6 helps you out (again!). + +== USAGE +https://docs.perl6.org/language/functions#index-entry-USAGE[`USAGE`] is a +default sub in Perl 6. It gets called if the program contains one or more +`MAIN` definitions, but none of them match a valid invocation of the program. + +[source,sh] +---- +perl6 foo.p6 --help +Usage: + foo.p6 + foo.p6 +---- + +The `USAGE` here gets called because `--help` isn't a parameter that is +supported by the `MAIN` definitions this program has. You can see that it took +over the variable name `$input` to show `` in the usage text. This is +already decent documentation, compared to the average program. And you get it +for free. But what if this were only the tip of the iceberg? + +== Improving the `USAGE` output +Consider the following sample program: + +[TIP] +==== +This program was saved as `mkpatch` for me, but the actual name doesn't matter. +==== + +[source,perl6] +---- +#! /usr/bin/env perl6 + +use v6.c; + +sub MAIN ($file, *@file, :$output, :$clobber) { * } +---- + +[NOTE] +==== +The `*` in front of `@file` makes it a +https://docs.perl6.org/type/Signature#Slurpy_(A.K.A._Variadic)_Parameters["slurpy" +parameter]. This means it will "slurp" all remaining arguments into `@files`. +==== + +Try running it, and check the output it produces: + +[source,sh] +---- +perl6 mkpatch --help +Usage: + mkpatch [--output=] [--clobber=] [ ...] +---- + +One of the first things to notice is that named arguments will be represented +as command line options, `:$output` becomes `--output` and `:$clobber` becomes +`--clobber`. These are optional commands to pass to the program, just as how +they'd be optional arguments to any Perl 6 subroutine, and accessible by their +name. + +Another thing to notice is that I use the name `file` twice in `MAIN` 's +signature: `$file` and `*@file`. Since they're using different sigils (`$` and +`@`), this is allowed in Perl 6. While I wouldn't recommend doing this too +often in your programs, there are situations, like this one, where it can make +sense. + +If we'd use only the `$file`, we can only work with a single file argument. If +we'd use only `*@file`, the program would run "correctly" if there are no files +supplied. But by using them both, we can require one or more files to be passed +to our program. + +=== Types +`MAIN` 's parameters can be typed, like any other subroutine. These types will +also be incorporated in the `USAGE` output. Let's add some types to the +parameters of the `MAIN` we defined earlier. + +[source,perl6] +---- +#! /usr/bin/env perl6 + +use v6.c; + +sub MAIN ( + Str:D $file, + *@file, + Str:D :$output = ".", + Bool:D :$clobber = False, +) { * } +---- + +The options have been given default values as well, so they can remain +optional. The `*@file` parameter can't get a type, as this is not supported +for slurpy parameters. The `$file` is now required to be a string. The `USAGE` +output will differ based on the new information available to it: + +[source] +---- +Usage: + mkpatch [--output=] [--clobber] [ ...] +---- + +You can see that `--output` now expects a `Str` instead of `Any`. The +`--clobber` doesn't show any argument type anymore, because it's a Boolean +switch as indicated by the `Bool` type. If `--clobber` is given, `$clobber` +will be set to `True`. If it's missing, it will be set to `False`. + +=== POD +Using https://docs.perl6.org/language/pod[`Pod 6`], you can improve both your +code with useful comments, and improve the output of `USAGE`. We can add Pod 6 +comments to the `MAIN` sub, and to each individual argument. A comment will be +parsed as Pod 6 if it starts with `\#|` or `\#=`. The former will be attached +to the code that's after the comment, whereas the latter will be attached to +the code before the comment. I personally prefer the `\#|` variant, and will +stick to using that one. + +Our updated code will become as follows: + +[source,perl6] +---- +#! /usr/bin/env perl6 + +use v6.c; + +#| Some description of our program. +sub MAIN ( + #| Input file. + Str:D $file, + + #| Additional input files. + *@file, + + #| Directory to store the output files in. Defaults to current directory. + Str:D :$output = ".", + + #| Whether to clobber existing output files. Defaults to False. + Bool:D :$clobber = False, +) { * } +---- + +Now, the `USAGE` output of your program has grown quite a bit, with +descriptions for each individual parameter and for the application itself. + +[source] +---- +Usage: + mkpatch [--output=] [--clobber] [ ...] -- Some description of our program. + + Input file. + [ ...] Additional input files. + --output= Directory to store the output files in. Defaults to current directory. + --clobber Whether to clobber existing output files. Defaults to False. +---- + +Now you have some really good documentation for both the user _and_ future +developers of your application. And you don't even have to worry about keeping +documentation for either in sync with the other. + +=== Shorthand options +Sometimes you want to have a shorthand variant of a certain option. A common +example would be `-f` as a shorthand for `--force`, for instance. This can +easily be achieved in the signature of the `MAIN` sub in Perl 6. + +Since this sample application doesn't have a `:$force` argument, I'll use a +different argument for the example. Let's take `:$clobber`, the argument on +whether to overwrite existing files. This could be shortened to `-c`. To get +this effect, update the sample code to have the following signature: + +[source,perl6] +---- +#! /usr/bin/env perl6 + +use v6.c; + +#| Some description of our program. +sub MAIN ( + #| Input file. + Str:D $file, + + #| Additional input files. + *@file, + + #| Directory to store the output files in. Defaults to current directory. + Str:D :$output = ".", + + #| Whether to clobber existing output files. Defaults to False. + Bool:D :c(:$clobber) = False, +) { * } +---- + +If you try to run this application with `--help` once more, you'll see that the +`clobber` option can now optionally be called as `-c`. + +[source] +---- +Usage: + mkpatch [--output=] [-c|--clobber] [ ...] -- Some description of our program. + + Input file. + [ ...] Additional input files. + --output= Directory to store the output files in. Defaults to current directory. + -c|--clobber Whether to clobber existing output files. Defaults to False. +---- + +== Conclusion +Parsing the positional arguments and options is a breeze with the `MAIN` +subroutine, whereas documentation on how to use it is provided for free by the +default `USAGE` subroutine. These functionalities really make Perl 6 a great +language to write command line applications in.