From c455896ae9e69e2498742ff795e7886dee1ffa23 Mon Sep 17 00:00:00 2001
From: Patrick Spek <p.spek@tyil.nl>
Date: Fri, 5 Feb 2021 09:55:25 +0100
Subject: Move source files into src

---
 ...l6-introduction-to-application-programming.adoc | 784 +++++++++++++++++++++
 1 file changed, 784 insertions(+)
 create mode 100644 src/_posts/2018-03-20-perl6-introduction-to-application-programming.adoc

(limited to 'src/_posts/2018-03-20-perl6-introduction-to-application-programming.adoc')

diff --git a/src/_posts/2018-03-20-perl6-introduction-to-application-programming.adoc b/src/_posts/2018-03-20-perl6-introduction-to-application-programming.adoc
new file mode 100644
index 0000000..fc00bd3
--- /dev/null
+++ b/src/_posts/2018-03-20-perl6-introduction-to-application-programming.adoc
@@ -0,0 +1,784 @@
+---
+title: "Perl 6 - Introduction to application programming"
+date: 2018-03-20 11:08:00
+tags: Tutorial Perl6 Assixt GTK Programming Raku
+layout: post
+authors:
+  - ["Patrick Spek", "https://tyil.nl"]
+---
+= Perl 6 - Introduction to application programming
+:toc: preamble
+
+In this tutorial, I'll be guiding you through creating a simple application in
+Perl 6. If you don't have Perl 6 installed yet, get the
+http://rakudo.org/how-to-get-rakudo/[Rakudo Star] distribution for your OS.
+Alternatively, you can use the https://hub.docker.com/_/rakudo-star/[Docker
+image].
+
+The application itself will be a simple dice-roller. You give it a number of
+dice to roll, and the number of sides the die has. We'll start off by creating
+it as a console application, then work to make it a GUI as well with the
+`GTK::Simple` module.
+
+== Preparation
+First, you'll want to install the libgtk headers. How to get these depends on
+your distro of choice. For Debian-based systems, which includes Ubuntu and
+derivatives, this command would be the following `apt` invocation:
+
+[source]
+----
+$ apt install libgtk-3-dev
+----
+
+For other distros, please consult your documentation.
+
+To ease up module/application building, I'll use `App::Assixt`. This module
+eases up on common tasks required for building other modules or applications.
+So we'll start by installing this module through `zef`.
+
+[source]
+----
+$ zef install App::Assixt
+----
+
+[NOTE]
+====
+You may need to rehash your `$PATH` as well, which can be done using `hash -r`
+on `bash`, or `rehash` for `zsh`. For other shells, consult your manual.
+====
+
+Next up, we can use `assixt` to create the new skeleton of our application,
+with the `new` subcommand. This will ask for some user input, which will be
+recorded in the `META6.json`, a json-formatted file to keep track of meta
+information about the module.  `assixt` should take care of this file for you,
+so you never need to actually deal with it.
+
+[source]
+----
+$ assixt new
+----
+
+=== assixt input
+Since the `assixt new` command requires some input, I'll walk through these
+options and explain how these would affect your eventual application.
+
+==== Name of the module
+This is the name given to the module. This will be used for the directory name,
+which by default in `assixt` will be `perl6-` prepended to a lower-case version
+of the module name. If you ever wish to make a module that is to be shared in
+the Perl 6 ecosystem, this should be unique across the entire ecosystem.  If
+you're interested in some guidelines, the
+https://pause.perl.org/pause/query?ACTION=pause_namingmodules[PAUSE guidelines]
+seem to apply pretty well to Perl 6 as well.
+
+For this application, we'll use `Local::App::Dicer`, but you can use whatever
+name you'd prefer here.
+
+==== Your name
+Your name. This will be used as the author's name in the `META6.json`. It is
+used to find out who made it, in order to report issues (or words of praise,
+of course).
+
+==== Your email address
+Your email address. Like your name, it will be used in case someone has to
+contact you in regards off the module.
+
+==== Perl 6 version
+This defaults to `c` right now, and you can just hit enter to accept it. In the
+future, there will be a Perl 6.d available as well, in which case you can use
+this to indicate you want to use the newer features introduced in 6.d. This is
+not the case yet, so you just want to go with the default `c` value here.
+
+==== Module description
+A short description of your module, preferably a single sentence. This is
+useful to people wondering what the module is for, and module managers can show
+to the user.
+
+==== License key
+This indicates the license under which your module is distributed. This
+defaults to `GPL-3.0`, which I strongly recommend to use. The de-facto
+default seems to be `Artistic-2.0`, which is also used for Perl 6 itself.
+
+This identifier is based on the https://spdx.org/licenses/[SPDX license list].
+Anything not mentioned in this list is not acceptable. #TODO Clarify why
+
+== Writing your first test
+With the creation of the directory structure and metadata being taken care of
+by `assixt`, we can now start on writing things. Tests are not mandatory, but
+are a great tool for quickly checking if everything works. If you make larger
+applications, it really helps not having to manually test anything. Another
+benefit is that you can quickly see if your changes, or those of someone else,
+break anything.
+
+Creating the base template for tests, `assixt` can help you out again: `assixt
+touch` can create templates in the right location, so you don't have to deal
+with it. In this case we want to create a test, which we'll call "basic".
+
+[source]
+----
+$ assixt touch test basic
+----
+
+This will create the file `t/basic.t` in your module directory. Its contents
+will look as follows:
+
+[source,perl6]
+----
+#! /usr/bin/env perl6
+
+use v6.c;
+
+use Test;
+
+ok True;
+
+done-testing;
+
+# vim: ft=perl6
+----
+
+The only test it has right now is `ok True`, which will always pass testing. We
+will change that line into something more usable for this application:
+
+[source,perl6]
+----
+use Local::App::Dicer;
+
+plan 2;
+
+subtest "Legal rolls", {
+	plan 50;
+
+	for 1..50 {
+		ok 1 ≤ roll($_) ≤ $_, "Rolls between 1 and $_";
+	}
+}
+
+subtest "Illegal rolls", {
+	plan 3;
+
+	throws-like { roll(0) }, X::TypeCheck::Binding::Parameter, "Zero is not accepted";
+	throws-like { roll(-1) }, X::TypeCheck::Binding::Parameter, "Negative rolls are not accepted";
+	throws-like { roll(1.5) }, X::TypeCheck::Binding::Parameter, "Can't roll half sides";
+}
+----
+
+[NOTE]
+====
+Perl 6 allows mathematical characters to make your code more concise, as with
+the ≤ in the above block. If you use http://www.vim.org/[vim], you can make use
+of the https://github.com/vim-perl/vim-perl6[vim-perl6] plugin, which has an
+option to change the longer, ascii-based ops (in this case `\<=`) into the
+shorter unicode based ops (in this case `≤`). This specific feature requires
+`let g:perl6_unicode_abbrevs = 1` in your `vimrc` to be enabled with
+`vim-perl6`.
+
+If that's not an option, you can use a
+https://en.wikipedia.org/wiki/Compose_key[compose key]. If that is not viable
+either, you can also stick to using the ascii-based ops. Perl 6 supports both
+of them.
+====
+
+This will run 53 tests, split up in two
+https://docs.perl6.org/language/testing#Grouping_tests[subtests]. Subtests are
+used to logically group your tests. In this case, the calls that are correct
+are in one subtest, the calls that should be rejected are in another.
+
+The `plan` keywords indicate how many tests should be run. This will help spot
+errors in case your expectations were not matched. For more information on
+testing, check out https://docs.perl6.org/language/testing[the Perl 6 docs on
+testing].
+
+We're making use of two test routines, `ok` and `throws-like`. `ok` is a
+simple test: if the given statement is truthy, the test succeeds. The other
+one, `throws-like`, might require some more explanation. The first argument it
+expects is a code block, hence the `{ }`. Inside this block, you can run any
+code you want. In this case, we run code that we know shouldn't work. The
+second argument is the exception it should throw. The test succeeds if the
+right exception is thrown. Both `ok` and `throws-like` accept a descriptive
+string as optional last argument.
+
+=== Running the tests
+A test is useless if you can't easily run it. For this, the `prove` utility
+exists. You can use `assixt test` to run these tests properly as well, saving
+you from having to manually type out the full `prove` command with options.
+
+[source]
+----
+$ assixt test
+----
+
+You might notice the tests are currently failing, which is correct. The
+`Local::App::Dicer` module doesn't exist yet to test against. We'll be working
+on that next.
+
+[NOTE]
+====
+For those interested, the command run by `assixt test` is `prove -e "perl6
+-Ilib" t`. This will include the `lib` directory into the `PERL6PATH` to be
+able to access the libraries we'll be making. The `t` argument specifies the
+directory containing the tests.
+====
+
+== Creating the library
+Again, let's start with a `assixt` command to create the base template. This
+time, instead of `touch test`, we'll use `touch lib`.
+
+[source]
+----
+$ assixt touch unit Local::App::Dicer
+----
+
+This will generate a template file at `lib/Local/App/Dicer.pm6` which some
+defaults set. The file will look like this.
+
+[source,perl6]
+----
+#! /usr/bin/env false
+
+use v6.c;
+
+unit module Local::App::Dicer;
+----
+
+The first line is a https://en.wikipedia.org/wiki/Shebang_(Unix)[shebang]. It
+informs the shell what to do when you try to run the file as an executable
+program. In this case, it will run `false`, which immediately exits with a
+non-success code. This file needs to be run as a Perl 6 module file, and
+running it as a standalone file is an error.
+
+The `use v6.c` line indicates what version of Perl 6 should be used, and is
+taken from the `META6.json`, which was generated with `assixt new`.  The last
+line informs the name of this module, which is `Local::App::Dicer`. Beneath
+this, we can add subroutines, which can be exported. These can then be accessed
+from other Perl 6 files that `use` this module.
+
+=== Creating the `roll` subroutine
+Since we want to be able to `roll` a die, we'll create a subroutine to do
+exactly that. Let's start with the signature, which tells the compiler the name
+of the subroutine, which arguments it accepts, their types and what type the
+subroutine will return.
+
+[TIP]
+====
+Perl 6 is gradually typed, so all type information is optional. The subroutine
+arguments are optional too, but you will rarely want a subroutine that doesn't
+have an argument list.
+====
+
+[source,perl6]
+----
+sub roll($sides) is export
+{
+	$sides
+}
+----
+
+Let's break this down.
+
+- `sub` informs the compiler we're going to create a subroutine.
+- `roll` is the name of the subroutine we're going to create.
+- `$sides` defines an argument used by the subroutine.
+- `is export` tells the compiler that this subroutine is to be exported. This
+  allows access to the subroutine to another program that imports this module
+  through a `use`.
+- `{ $sides }` is the subroutine body. In Perl 6, the last statement is also
+  the return value in a code block, thus this returns the value of $sides. A
+  closing `;` is also not required for the last statement in a block.
+
+If you run `assixt test` now, you can see it only fails 1/2 subtests:
+
+[source]
+----
+# TODO: Add output of failing tests
+----
+
+Something is going right, but not all of it yet. The 3 tests to check for
+illegal rolls are still failing, because there's no constraints on the input of
+the subroutine.
+
+=== Adding constraints
+The first constraint we'll add is to limit the value of `$sides` to an `Int:D`.
+The first part of this constraint is common in many languages, the `Int` part.
+The `:D` requires the argument to be **defined**. This forces an actual
+existing instance of `Int`, not a `Nil` or undefined value.
+
+[source,perl6]
+----
+sub roll(Int:D $sides) is export
+----
+
+Fractional input is no longer allowed, since an `Int` is always a round number.
+But an `Int` is still allowed to be 0 or negative, which isn't possible in a
+dice roll. Nearly every language will make you solve these two cases in the
+subroutine body. But in Perl 6, you can add another constraint in the signature
+that checks for exactly that:
+
+[source,perl6]
+----
+sub roll(Int:D $sides where $sides > 0) is export
+----
+
+The `where` part specifies additional constraints, in this case `$sides > 0`.
+So now, only round numbers larger than 0 are allowed. If you run `assixt test`
+again, you should see all tests passing, indicating that all illegal rolls are
+now correctly disallowed.
+
+=== Returning a random number
+So now that we can be sure that the input is always correct, we can start on
+making the output more random. In Perl 6, you can take a number and call
+`.rand` on it, to get a random number between 0 and the value of the number you
+called it on. This in turn can be rounded up to get a number ranging from 1 to
+the value of the number you called `.rand` on. These two method calls can also
+be changed to yield concise code:
+
+[source,perl6]
+----
+sub roll(Int:D $sides where $sides > 0) is export
+{
+	$sides.rand.ceiling
+}
+----
+
+That's all we need from the library itself. Now we can start on making a usable
+program out of it.
+
+== Adding a console interface
+First off, a console interface. `assixt` can `touch` a starting point for an
+executable script as well, using `assixt touch bin`:
+
+[source]
+----
+$ assixt touch bin dicer
+----
+
+This will create the file `bin/dicer` in your repository, with the following
+template:
+
+[source,perl6]
+----
+#! /usr/bin/env perl6
+
+use v6.c;
+
+sub MAIN
+{
+	…
+}
+----
+
+The program will run the `MAIN` sub by default. We want to slightly change this
+`MAIN` signature though, since we want to accept user input. And it just so
+happens that you can specify the command line parameters in the `MAIN`
+signature in Perl 6. This lets us add constraints to the parameters and give
+them better names with next to no effort. We want to accept two numbers, one
+for the number of dice, and one for the number of sides per die:
+
+[source,perl6]
+----
+sub MAIN(Int:D $dice, Int:D $sides where { $dice > 0 && $sides > 0 })
+----
+
+Here we see the `where` applying constraints again. If you try running this
+program in its current state, you'll have to run the following:
+
+[source]
+----
+$ perl6 -Ilib bin/dicer
+Usage:
+  bin/dicer <dice> <sides>
+----
+
+This will return a list of all possible ways to invoke the program. There's one
+slight problem right now. The usage description does not inform the user that
+both arguments need to be larger than 0. We'll take care of that in a moment.
+First we'll make this part work the way we want.
+
+To do that, let's add a `use` statement to our `lib` directory, and call the
+`roll` function we created earlier. The `bin/dicer` file will come to look as
+follows:
+
+[source,perl6]
+----
+#! /usr/bin/env perl6
+
+use v6.c;
+
+use Local::App::Dicer;
+
+sub MAIN(Int:D $dice, Int:D $sides where { $dice > 0 && $sides > 0 })
+{
+	say $dice × roll($sides)
+}
+----
+
+[NOTE]
+====
+Just like the `≤` character, Perl 6 allows to use the proper multiplication
+character `×` (this is not the letter `x`!). You can use the more widely known
+`*` for multiplication as well.
+====
+
+If you run the program with the arguments `2` and `20` now, you'll get a random
+number between 2 and 40, just like we expect:
+
+[source]
+----
+$ perl6 -Ilib bin/dicer 2 20
+18
+----
+
+=== The usage output
+Now, we still have the trouble of illegal number input not clearly telling
+what's wrong. We can do a neat trick with
+https://docs.perl6.org/language/functions#index-entry-USAGE[the USAGE sub] to
+achieve this. Perl 6 allows a subroutine with the name `USAGE` to be defined,
+overriding the default behaviour.
+
+Using this, we can generate a friendlier message informing the user what they
+need to supply more clearly. The `USAGE` sub would look like this:
+
+[source,perl6]
+----
+sub USAGE
+{
+	say "Dicer requires two positive, round numbers as arguments."
+}
+----
+
+If you run the program with incorrect parameters now, it will show the text
+from the `USAGE` subroutine. If the parameters are correct, it will run the
+`MAIN` subroutine.
+
+You now have a working console application in Perl 6!
+
+== Making a simple GUI
+But that's not all. Perl 6 has a module to create GUIs with the
+https://www.gtk.org/[GTK library] as well. For this, we'll use the
+https://github.com/perl6/gtk-simple[`GTK::Simple`] module.
+
+You can add this module as a dependency to the `Local::App::Dicer` repository
+with `assixt` as well, using the `depend` command. By default, this will also
+install the dependency locally so you can use it immediately.
+
+[source]
+----
+$ assixt depend GTK::Simple
+----
+
+=== Multi subs
+Next, we could create another executable file and call it `dicer-gtk`. However,
+I can also use this moment to introduce
+https://docs.perl6.org/language/glossary#index-entry-multi-method[multi
+methods]. These are subs with the same name, but differing signatures. If a
+call to such a sub could potentially match multiple signatures, the most
+specific one will be used. We will add another `MAIN` sub, which will be called
+when `bin/dicer` is called with the `--gtk` parameter.
+
+We should also update the `USAGE` sub accordingly, of course. And while we're
+at it, let's also include the `GTK::Simple` and `GTK::Simple::App` modules. The
+first pulls in all the different GTK elements we will use later on, while the
+latter pulls in the class for the base GTK application window.  The updated
+`MAIN`, `USAGE` and `use` parts will now look like this:
+
+[source,perl6]
+----
+use Local::App::Dicer;
+use GTK::Simple;
+use GTK::Simple::App;
+
+multi sub MAIN(Int:D $dice, Int:D $sides where { $dice > 0 && $sides > 0 })
+{
+	say $dice × roll($sides)
+}
+
+multi sub MAIN(Bool:D :$gtk where $gtk == True)
+{
+	# TODO: Create the GTK version
+}
+
+sub USAGE
+{
+	say "Launch Dicer as a GUI with --gtk, or supply two positive, round numbers as arguments.";
+}
+----
+
+There's a new thing in a signature header here as well, `:$gtk`. The `:` in
+front of it makes it a named argument, instead of a positional one. When used
+in a `MAIN`, this will allow it to be used like a long-opt, thus as `--gtk`.
+Its use in general subroutine signatures is explained in the next chapter.
+
+Running the application with `--gtk` gives no output now, because the body only
+contains a comment. Let's fix that.
+
+=== Creating the window
+First off, we require a `GTK::Simple::App` instance. This is the main window,
+in which we'll be able to put elements such as buttons, labels, and input
+fields. We can create the `GTK::Simple::App` as follows:
+
+[source,perl6]
+----
+my GTK::Simple::App $app .= new(title => "Dicer");
+----
+
+This one line brings in some new Perl 6 syntax, namely the `.=` operator.
+There's also the use of a named argument in a regular subroutine.
+
+The `.=` operator performs a method on the variable on the left. In our case,
+it will call the `new` subroutine, which creates a new instance of the
+`GTK::Simple::App` class. This is commonly referred to as the **constructor**.
+
+The named argument list (`title \=> "Dicer"`) is another commonly used feature
+in Perl 6. Any method can be given a non-positional, named parameter. This is
+done by appending a `:` in front of the variable name in the sub signature.
+This has already been used in our code, in `multi sub MAIN(Bool :$gtk where
+$gtk == True)`. This has a couple of benefits, which are explained in the
+https://docs.perl6.org/type/Signature#index-entry-positional_argument_%28Signature%29_named_argument_%28Signature%29[Perl
+6 docs on signatures].
+
+=== Creating the elements
+Next up, we can create the elements we'd like to have visible in our
+application window. We needed two inputs for the console version, so we'll
+probably need two for the GUI version as well. Since we have two inputs, we
+want labels for them. The roll itself will be performed on a button press.
+Lastly, we will want another label to display the outcome. This brings us to 6
+elements in total:
+
+- 3 labels
+- 2 entries
+- 1 button
+
+[source,perl6]
+----
+my GTK::Simple::Label $label-dice .= new(text => "Amount of dice");
+my GTK::Simple::Label $label-sides .= new(text => "Dice value");
+my GTK::Simple::Label $label-result .= new(text => "");
+my GTK::Simple::Entry $entry-dice .= new(text => 0);
+my GTK::Simple::Entry $entry-sides .= new(text => 0);
+my GTK::Simple::Button $button-roll .= new(label => "Roll!");
+----
+
+This creates all elements we want to show to the user.
+
+=== Show the elements in the application window
+Now that we have our elements, let's put them into the application window.
+We'll need to put them into a layout as well. For this, we'll use a grid. The
+`GTK::Simple::Grid` constructor takes pairs, with the key being a tuple
+containing 4 elements, and the value containing the element you want to show.
+The tuple's elements are the `x`, `y`, `w` and `h`, which are the x
+coordinates, y coordinates, width and height respectively. 
+
+This in turn takes us to the following statement:
+
+[source,perl6]
+----
+$app.set-content(
+	GTK::Simple::Grid.new(
+		[0, 0, 1, 1] => $label-dice,
+		[1, 0, 1, 1] => $entry-dice,
+		[0, 1, 1, 1] => $label-sides,
+		[1, 1, 1, 1] => $entry-sides,
+		[0, 2, 2, 1] => $button-roll,
+		[0, 3, 2, 1] => $label-result,
+	)
+);
+----
+
+Put a `$app.run` beneath that, and try running `perl6 -Ilib bin/dicer --gtk`.
+That should provide you with a GTK window with all the elements visible in the
+position we want. To make it a little more appealing, we can add a
+`border-width` to the `$app`, which adds a margin between the border of the
+application window, and the grid inside the window.
+
+[source,perl6]
+----
+$app.border-width = 20;
+$app.run;
+----
+
+You may notice that there's no `()` after the `run` method call. In Perl 6,
+these are optional if you're not supplying any arguments any way.
+
+=== Binding an action to the button
+Now that we have a visible window, it's time to make the button perform an
+action. The action we want to execute is to take the values from the two
+inputs, roll the correct number of dice with the correct number of sides, and
+present it to the user.
+
+The base code for binding an action to a button is to call `.clicked.tap` on it,
+and provide it with a code block. This code will be executed whenever the
+button is clicked.
+
+[source,perl6]
+----
+$button-roll.clicked.tap: {
+};
+----
+
+You see we can also invoke a method using `:`, and then supplying its
+arguments. This saves you the trouble of having to add additional `( )` around
+the call, and in this case it would be annoying to have to deal with yet
+another set of parens.
+
+Next, we give the code block something to actually perform:
+
+[source,perl6]
+----
+$button-roll.clicked.tap: {
+	CATCH {
+		$label-result.text = "Can't roll with those numbers";
+	}
+
+	X::TypeCheck::Binding::Parameter.new.throw if $entry-dice.text.Int < 1;
+
+	$label-result.text = ($entry-dice.text.Int × roll($entry-sides.text.Int)).Str;
+};
+----
+
+There's some new things in this block of code, so let's go over these.
+
+- `CATCH` is the block in which we'll end up if an exception is thrown in this
+  scope. `roll` will throw an exception if the parameters are wrong, and this
+  allows us to cleanly deal with that.
+- `X::TypeCheck::Binding::Parameter.new.throw` throws a new exception of type
+  `X::TypeCheck::Binding::Parameter`. This is the same exception type as thrown
+  by `roll` if something is wrong. We need to check the number of dice manually
+  here, since `roll` doesn't take care of it, nor does any signature impose any
+  restrictions on the value of the entry box.
+- `if` behind another statement. This is something Perl 6 allows, and in some
+  circumstances can result in cleaner code. It's used here because it improves
+  the readability of the code, and to show that it's possible.
+
+== The completed product
+And with that, you should have a dice roller in Perl 6, with both a console and
+GTK interface. Below you can find the complete, finished sourcefiles which you
+should have by now.
+
+=== t/basic.t
+[source,perl6]
+----
+#! /usr/bin/env perl6
+
+use v6.c;
+
+use Test;
+use Local::App::Dicer;
+
+plan 2;
+
+subtest "Legal rolls", {
+	plan 50;
+
+	for 1..50 {
+		ok 1 ≤ roll($_) ≤ $_, "Rolls between 1 and $_";
+	}
+}
+
+subtest "Illegal rolls", {
+	plan 3;
+
+	throws-like { roll(0) }, X::TypeCheck::Binding::Parameter, "Zero is not accepted";
+	throws-like { roll(-1) }, X::TypeCheck::Binding::Parameter, "Negative rolls are not accepted";
+	throws-like { roll(1.5) }, X::TypeCheck::Binding::Parameter, "Can't roll half sides";
+}
+
+done-testing;
+
+# vim: ft=perl6
+----
+
+=== lib/Local/App/Dicer.pm6
+[source,perl6]
+----
+#! /usr/bin/env false
+
+use v6.c;
+
+unit module Local::App::Dicer;
+
+sub roll(Int:D $sides where $sides > 0) is export
+{
+	$sides.rand.ceiling;
+}
+----
+
+=== bin/dicer
+[source,perl6]
+----
+#! /usr/bin/env perl6
+
+use v6.c;
+
+use Local::App::Dicer;
+use GTK::Simple;
+use GTK::Simple::App;
+
+multi sub MAIN(Int:D $dice, Int:D $sides where { $dice > 0 && $sides > 0 })
+{
+	say $dice × roll($sides)
+}
+
+multi sub MAIN(Bool:D :$gtk where $gtk == True)
+{
+	my GTK::Simple::App $app .= new(title => "Dicer");
+	my GTK::Simple::Label $label-dice .= new(text => "Number of dice");
+	my GTK::Simple::Label $label-sides .= new(text => "Number of sides per die");
+	my GTK::Simple::Label $label-result .= new(text => "");
+	my GTK::Simple::Entry $entry-dice .= new(text => 0);
+	my GTK::Simple::Entry $entry-sides .= new(text => 0);
+	my GTK::Simple::Button $button-roll .= new(label => "Roll!");
+
+	$app.set-content(
+		GTK::Simple::Grid.new(
+			[0, 0, 1, 1] => $label-dice,
+			[1, 0, 1, 1] => $entry-dice,
+			[0, 1, 1, 1] => $label-sides,
+			[1, 1, 1, 1] => $entry-sides,
+			[0, 2, 2, 1] => $button-roll,
+			[0, 3, 2, 1] => $label-result,
+		)
+	);
+
+	$button-roll.clicked.tap: {
+		CATCH {
+			$label-result.text = "Can't roll with those numbers";
+		}
+
+		X::TypeCheck::Binding::Parameter.new.throw if $entry-dice.text.Int < 1;
+
+		$label-result.text = ($entry-dice.text.Int × roll($entry-sides.text.Int)).Str;
+	};
+
+	$app.border-width = 20;
+
+	$app.run;
+}
+
+sub USAGE
+{
+	say "Launch Dicer as a GUI with --gtk, or supply two positive, round numbers as arguments.";
+}
+----
+
+== Installing your module
+Now that you have a finished application, you probably want to install it as
+well, so you can run it by calling `dicer` in your shell. For this, we'll be
+using `zef`.
+
+To install a local module, tell `zef` to try and install the local directory
+you're in:
+
+[source]
+----
+$ zef install .
+----
+
+This will resolve the dependencies of the local module, and then install it.
+You should now be able to run `dicer` from anywhere.
+
+[WARNING]
+====
+With most shells, you have to "rehash" your `$PATH` as well. On `bash`, this is
+done with `hash -r`, on `zsh` it's `rehash`. If you're using any other shell,
+please consult the manual.
+====
-- 
cgit v1.1

