February 2016 Archives

How to Make a Perl 6 Module (Bit Rot Thursday)

Happy Bit Rot Thursday! This week I'm taking care of fixing minor issues with packaging of my Perl 6 modules, and so, I'll talk about the general process of releasing a Perl 6 module. Let's dive in!

Prelude

Perl 6 is a brand new language, so there's yet no well-established module system like what Perl 5 has, but there is a work in progress. Thus, I'll first describe the process for our temporary GitHub-based system and then I'll talk about the PAUSE-based system that's being worked on.

There are some tools and helpers available to help with the process of module development, but they are beyond the scope of this post.

Terms (and no conditions)

  • Ecosystem—collection of Perl 6 modules and scripts, viewable at modules.perl6.org with META-data listed in the perl6/ecosystem repo
  • panda—a Perl 6 module installer
  • zef—an alternative Perl 6 module installer
  • repo—the files of a project hosted somewhere, like on GitHub
  • PR—abbreviation for "Pull Request"; a request on GitHub to apply a change to some files in a project

So, you want to write a Perl 6 module?

If so, awesome! Congratulations on your decision to become a member of the elite, exclusive, limited-time, offer-expires-soon team of about 130 developers who currently own the 549 modules that comprise the Perl 6 Ecosystem.

There are plenty of things that need to be written and if you're still having trouble coming up with ideas for something to code, check out the Most Wanted list.

You can publish Perl 6 modules as well as scripts (executables).

The Files

META6.json
README.md
.travis.yml
.gitignore
bin/baker.p6
lib/Bread/Baker.pm6
resources/recipe.txt
t/00-test-bake.t
xt/00-ensure-bread-is-tasty.t

The above shows the possible files and directories your distribution would have. The bin/ directory is for executables, lib/ is for modules, resources/ is for additional resources, such as images or templates, t/ is for tests to be run by the user, and xt/ is for your author tests that are not run as part of the installation process. The documentation can be included in the same file as code in POD6 format, but since the current system uses GitHub, a README.md makes it much easier to read the docs.

Also, you are encouraged to enable Travis testing, hence the included .travis.yml file. You can use either a simple config file or a more advanced version written by ugexe.

Most important of all is the META6.json file. It's a distribution metafile in JSON format that specifies what stuff your distro provides, as well as its prerequisites and authorship information. You can learn what all the keys are for in S22 Speculation or look at a sample META file. This is a place where many errors happen, so I encourage you to use Test::META that will spot all the common mistakes.

Lastly, .gitignore is a file where you can list things for git to ignore and not include in your repo. For a start, you'll want to add single line lib/.precomp into it. This is the directory created by Rakudo to store precompiled files when you run your tests, for example, and you don't need to store it anywhere.

Add to Ecosystem (The Now)

Currently, the authors host their modules as repos on GitHub, so place your files there. It'll require some understanding of how to use git.

Grab a link to the raw view of your META file. You can get to it by clicking the "Raw" button in the top, right corner of the file view on GitHub. It'll be a link akin to https://raw.githubusercontent.com/zoffixznet/perl6-IO-MiddleMan/master/META6.json

arrow.png

Go to the META.list file in perl6/ecosystem repo. You can edit that file directly (and submit a PR) by clicking the pencil icon in top, right corner on GitHub, or fork the repo and submit a PR using other means. In that file, on a separate line, add the link to your dist's META file.

After 1–2 hours after your PR is merged, the build cron job will list your module on modules.perl6.org. If it's still missing, check the build log for any errors; you can just search the page for term [error]

Keep in mind that panda doesn't fetch a new ecosystem list on each run, so if you want to install your freshly-added module, you need to run panda update first.

Add to Ecosystem (The Future)

Perl 5's model goes something like this: you upload stuff on PAUSE, it gets propagated to all sorts of mirrors (CPAN), you can search for things using MetaCPAN, and you install those things from one of the mirrors using a CPAN client, like cpanm. Wouldn't it be sweet for Perl 6 folks to get in on that action?

Well, you can! Unless you're reading this after the world was destroyed by a nuclear catastrophe, you can log in onto PAUSE right this second and upload a Perl 6 dist.

Providing your dist looks proper and contains META6.json file, all you need to do is choose Perl6 as the Target Directory on the dist upload page.

Now, just because you uploaded a Perl 6 dist doesn't mean it'll show up on MetaCPAN; it's the whole point of specifying the Perl6 target dir. There will be a Perl 6 version of the MetaCPAN hosted elsewhere. That MetaCPAN will be a modified version of the Perl 5's MetaCPAN under the hood.

Currently, that work is being done by brave pioneers like jdv79, ranguard, and anyone else who I left out due to my ignorance. Having more Volunteers would certainly be helpful, and if you seek fame and recognition, you should stop by #perl6-toolchain channel on irc.Freenode.net and offer a helping hand.

Hopefully, you found this article helpful and I await your contributions to the Perl 6 Ecosystem!

Perl 6: Shortcuts (Part 1)

Programming is a task where laziness is a virtue. We write modules to avoid repeatedly writing subroutines. We write subroutines to avoid repeatedly writing loops. We write loops to avoid repeatedly writing code... And there's another level of laziness: we use language shortcuts to avoid writing too much code.

Shortcuts are a controversial subject. Some say they make code faster to write and easier to read. Some say they make it harder to understand for people unfamiliar with those shortcuts. So this article is merely telling you about shortcuts and it's up to you to decide whether to use them or to avoid them. Let's begin, shall we!

Public Getter/Setter for Class Attributes

The concept of a "getter" and "setter" is common in many languages: you have a "thing" in your class and you write a method to set or get the value of that thing. In verbose Perl 6, such a set up might look something like this:

class Foo {
    has $!attr = 42;
    method attr is rw { $!attr }
}

my  $obj = Foo.new;
say $obj.attr;
    $obj.attr = 72;
say $obj.attr;

# OUTPUT>>:
# 42
# 72

That looks pretty concise as it is, but public attributes are common enough to make writing even this bit of code annoying. Which is why the $. twigil exists. Using it alone creates a "getter"; if you want a "setter" as well, use the is rw trait:

class Foo { has $.attr is rw = 42; }
my  $obj = Foo.new;
say $obj.attr;
    $obj.attr = 72;
say $obj.attr;

# OUTPUT>>:
# 42
# 72

We changed the $! twigil on our attribute to $. twigil and it took care of creating a public method for us. Moving on!

Omiting Parentheses on method calls

It's not uncommon to see code like this, where you have a whole ton of parentheses at the end. Be sure they all match up!!

$foo.log( $obj.nukanate( $foo.grep(*.bar).map(*.ber) ) );

For those who are reminded of a popular webcomic, Perl 6 has an alternative:

$foo.log: $obj.nukanate: $foo.grep(*.bar).map: *.ber;

If a method is last in the method call chain, you can omit its parentheses and use a colon : instead. Except for .grep, all of our calls above are "last in the chain," so that was quite a bit of parentheses we got rid of. Sometimes I also like to start the thing following the colon on a new line.

And just a note: you can always omit parenthesis on method calls if you're not supplying any arguments; no semicolons are needed either.

Commaless Named Arguments

If you're calling a method or a sub and are providing only named arguments, you can omit commas between the arguments. Sometimes, I like to stack each argument on a new line as well:

class Foo {
    method baz (:$foo, :$bar, :$ber) { say "[$foo, $bar, $ber]" }
}
    sub    baz (:$foo, :$bar, :$ber) { say "[$foo, $bar, $ber]" }

Foo.baz:
    :foo(42)
    :bar(72)
    :ber(100);

baz :foo(42) :bar(72) :ber(100);

# OUTPUT>>:
# [42, 72, 100]
# [42, 72, 100]

Again, this works when you are providing only named arguments. There are many, many other places where you'd be using the same form to provide arguments or Pairs, but you can't omit commas there.

Integers in Named Arguments/Pairs

Looking at the last code example, it's a bit parentheses-heavy. So there's another shortcut: if the argument or Pair takes a positive integer as a value, simply write it between the colon and the name of the key:

say DateTime.new: :2016year :2month :1day :16hour :32minute;

# OUTPUT>>:
# 2016-02-01T16:32:00Z

This is one of those things that look jarring when you first learn it, but you get used to it quite fast. It also reads a lot like English:

my  %ingredients = :4eggs, :2sticks-of-butter, :4cups-of-sugar;
say %ingredients;

# OUTPUT>>:
# cups-of-sugar => 4, eggs => 4, sticks-of-butter => 2

Booleans in Named Arguments/Pairs

If we have a shortcut for Ints in named arguments, it'd be daft not to have one for Booleans too. And there is one: use the name of the key by itself to indicate True; insert an exclamation mark between the key and the colon to indicate False:

sub foo (:$bar, :$ber) { say "$bar, $ber" }
foo :!bar :ber;

my  %hash = :!bar, :ber;
say %hash;

# OUTPUT>>:
# False, True
# bar => False, ber => True

Note: this applies to adverbs as well!

Lists in Named Arguments/Pairs

If you're supplying a quote-word construct to a named argument/pair that expects something listy, you can omit parentheses; just don't use any spaces between the key and the the quote-words:

sub foo (:@args) { say @args }
foo :args<foo bar ber>;

my  %hash = :ingredients<milk eggs butter>;
say %hash;

# OUTPUT>>:
# (foo bar ber)
# ingredients => (milk eggs butter)

Pass-through of variables to Named Arguments/Pairs;

Did you think we were done with the named args? There's one more cool shortcut: s'pose you have a variable and it has the same name as the named argument... just pass it in by using the variable itself, instead of the key, after the colon:

sub hashify (:$bar, :@ber) {
    my %hash = :$bar, :@ber;
    say %hash;
}

my ( $bar, @ber ) = 42, (1..3);
hashify :$bar :@ber;

# OUTPUT>>:
# bar => 42, ber => [1..3]

Notice neither in the sub call nor in our hash creation are we duplicating the names of keys. They're derived from variable names.

Subs as method calls

If you have a sub you're dying to call as a method on something, just prefix it with an ampersand. The invocant will be the first positional argument, with all the other args passed as usual.

sub be-wise ($self, $who = 'Anonymous') { "Know your $self, $who!" }

'ABC'.&be-wise.say;
'ABC'.&be-wise('Zoffix').say;

# OUTPUT>>:
# Know your ABC, Anonymous!
# Know your ABC, Zoffix!

This is essentially a less-ugly way to call a .map in certain instances, but using a sub as a sub was meant to be used would likely win most of the time, in terms of readability.

sub be-wise ($self, $who = 'Anonymous') { "Know your $self, $who!" }

'ABC'.map({be-wise $_, 'Zoffix'})».say;
say be-wise 'ABC', 'Zoffix';

# OUTPUT>>:
# Know your ABC, Zoffix!
# Know your ABC, Zoffix!

For the sake of completeness, and not anything overly practical, know that you can also inline the call and even use a pointy block to set a signature!

'ABC'.&('Know your ' ~ *).say;
'ABC'.&( -> $self, $who = 'Anonymous' {"Know your $self, $who!"} )('Zoffix')
    .say;

# OUTPUT>>:
# Know your ABC
# Know your ABC, Zoffix!

Hyper Method Calls

Since we're on the topic of shortcuts for .map, keep the » hyper operator in mind. Using it before the dot of a method call indicates you want to call the following method on each element of the invocant, instead of the invocant itself. As with all fancy-pants operators, Perl 6 provides "Texas" variant for this operator as well, >>

(1, 2, 3)».is-prime.say;
(1, 2, 3)>>.is-prime.say;

# OUTPUT>>:
# (False True True)
# (False True True)

This one has a bonus too: while currently not yet implemented in Rakudo, the spec permits this operator to perform concurrently, so you can eventually see it perform the faster the more cores your box has!

Summary

  • Use $. twigil to declare public attributes
  • Use : instead of parentheses for giving arguments to a method call that is last in the chain
  • Method/sub calls with only named arguments do not need commas
  • Pass Int values by writing them between the key and the colon
  • Use key by itself to specify a True boolean value
  • Use key by itself, with ! between it and colon to specify a False boolean value
  • When value is a quote-word construct, write it right after the key, without any parentheses
  • When a variable has the same name as the key, use it directly as the key (including the sigil), without specifying any values
  • Prefix the name of a sub with & when calling it as a method.
  • Use » operator to call a method on each item in the list.

Conclusion

This isn't the full list of Perl 6 shortcuts and I'm sure I'm yet to learn some of them myself. This is why I named the article 'Part 1'. Do you know any cool and useful shortcuts you'd like to be included in subsequent parts? Post them in the comments!

About Zoffix Znet

user-pic I blog about Perl.