Day 8 — How to Make, Use, and Abuse Perl 6 Subsets

Ever reached for a Perl 6 type and found it good enough, but not perfect? Perhaps, you wanted an IntEven, StrPalindrome, or YourCustomClassWhereAttrFooIsBar. Never fear, Perl 6’s subsets are here!

What Are Subsets?

You can think of them as a refinement on some type and you can use them in most places where you’d use a type, such as in type constraints. To create one, use the subset keyword, along with a where keyword specifying your refinement:

    subset Even where * %% 2;
    say 1 ~~ Even; # False
    say 2 ~~ Even; # True

The WhateverStar * is the value being checked and the %% operator checks if that value is evenly divisible by 2. We can now use our subset on the right side of a smartmatch operator to check whether a value is an even number! Pretty awesome. What else can we do with it?

How about type-constraining a variable:

    my Even $x = 42; # all good

    $x = 43; # and this?
    # Type check failed in assignment to $x; expected Even but got Int (43)
    #   in block <unit> at script.p6 line 3

Or type-constraining input and output of a routine:

    sub takes-an-even-only (Even $x) { $x² }
    sub returns-an-even-only returns Even { $^x² }

    say takes-an-even-only   42; # 1764
    say returns-an-even-only 42; # 1764

    say takes-an-even-only   43;
    # Constraint type check failed for parameter '$x'
    #   in sub takes-an-even-only at script.p6 line 2
    #   in block <unit> at script.p6 line 8

    say returns-an-even-only 43;
    # Type check failed for return value; expected Even but got Int (1849)
    #   in sub returns-an-even-only at script.p6 line 3
    #   in block <unit> at script.p6 line 13

That’s all pretty sweet, but our Even accepts strings and other weird stuff:

    say '42.0000' ~~ Even; # True
    say class { method Real { 42 } }.new ~~ Even; # True

There’s a reason for that: we never specified what type we’re making a subset of, so by default, it used Any. Let’s fix that!

Getting Typical

If you want to create a subset based on some type, specify that type with the of keyword:

    subset IntEven of Int where * %% 2;

Now, before the where refinement even runs, the value we’re checking against must first pass the Int type constraint:

    say 42 ~~ IntEven; # True
    say 43 ~~ IntEven; # False
    say '42.0000' ~~ IntEven; # False
    say class { method Real { 42 } }.new ~~ IntEven; # False

We’re not limited to numerics! Let’s try a Str:

    subset StrPalindrome of Str where {
        .flip eq $_ given .comb(/\w+/).join.lc;
    }

    say ’Madam, I'm Adam.~~ StrPalindrome; # True
    say '1 on 1'  ~~ StrPalindrome; # False

We’re now using a more complex refinement in the where clause, using a code block. Just like the WhateverCode version with the *, the block receives the value to check as its argument, which it aliases to $_ topical variable. The code block tells us whether the argument is a palindrome, by returning a truthy or falsy value.

So how far can we go with the type constraints in our subsets?

Custom Made

We can type-constrain a subset using any class we have lying around! How about this one:

    class Awesome { has $.awesomeness-level }
    my $obj1 = Awesome.new: :10000awesomeness-level;
    my $obj2 = Awesome.new: :31337awesomeness-level;

We make a class called Awesome that has a public attribute called awesomeness-level. We also create two instances of that class, setting the awesomeness-level to 10000 in $obj1 and to 31337 in $obj1. So how about a subset that checks whether awesomeness-level is a prime number? It’s just a single line of code:

    subset AwesomePrime of Awesome where .awesomeness-level.is-prime;

    say $obj1 ~~ AwesomePrime; # False
    say $obj2 ~~ AwesomePrime; # True

The where block of the subset is “thunked,” which means the compiler takes an expression and turns it into a code block for us, so we don’t have to explicitly use a codeblock here, nor do we need a WhateverStar. The value being checked is in the $_ topical variable, which is what method calls use when you don’t specify what you’re calling them on. Thus, our subset expects a thing of type Awesome and then checks whether its awesomeness-level attribute is a prime number!

By using such subsets, you can create routines that only accept your custom objecs of a specific configuration. For example, in code of an IRC::Client bot, we can create a subset of IRC::Client::Message for messages that are received from bot admins, and then register events only for such messages:

    subset BotAdmin of IRC::Client::Message where .host eq conf<bot-admins>.any;

    multi method irc-to-me (BotAdmin $e where /:i ^ 'run' $<steps>=.+ $/ ) {
        ...
    }

The subset calls the subroutine that reads configuration and provides a list of bot admin hosts against which the host of the sender of the message is checked. We’re encapsulating the logic that checks we have an acceptable object to work with, and we’re able to call that logic even before we enter our method.

So if we can do that with subsets… is there anything we can’t do?

Time for Some Heavy Abuse!

Let’s do something crazy! A subroutine that fetches a link to a website and checks whether it contains a mention of Perl 6:

    use LWP::Simple;
    sub is-perl-site { LWP::Simple.get( $^website ).contains: 'Perl 6' }

There’s nothing crazy about that, you say? Then, how about we use that subroutine as a refiner in a subset where clause:

    subset PerlWebsite where &is-perl-site;

    say 'http://perl6.party' ~~ PerlWebsite; # True
    say 'http://lolcats.com' ~~ PerlWebsite; # False

In fact, we can make a routine that only accepts URLs to websites mentioning Perl 6:

    sub ain't-taking-non-perl-stuff (PerlWebsite $url) {
        say "Why! I can already tell $url is awesome!";
    }

    ain't-taking-non-perl-stuff 'http://perl6.party';
    # Why! I can already tell http://perl6.party is awesome!

    ain't-taking-non-perl-stuff 'http://lolcats.com';
    # Constraint type check failed for parameter '$url'
    #   in sub ain't-taking-non-perl-stuff at script.p6 line 8
    #   in block <unit> at script.p6 line 15

But do you notice something off? The error message is rather poor… Let’s improve it!

What we know so far is the where clause takes some code to run and if that code’s result is falsy, the typecheck will fail. That means inside the where we can know whether or not the typecheck will fail before we even return from it. Let’s put that to use:

    sub is-perl-site {
        given LWP::Simple.get( $^website ).contains: 'Perl 6' {
            when :so  { True }
            when :not {
                say ’This ain't no website containing "Perl 6"!‘;
                False;
            }
        }
    }

    subset PerlWebsite where &is-perl-site;

In the routine that checks a website mentions Perl 6, in the case when it does :not contain a mention of Perl 6, we say a helpful message, indicating what exactly was wrong. Let’s run this:

    sub ain't-taking-non-perl-stuff (PerlWebsite $url) {
        say "Why! I can already tell $url is awesome!";
    }

    ain't-taking-non-perl-stuff 'http://perl6.party';
    # Why! I can already tell http://perl6.party is awesome!

    ain't-taking-non-perl-stuff 'http://lolcats.com';
    # This ain't no website containing "Perl 6"!
    # This ain't no website containing "Perl 6"!
    # Constraint type check failed for parameter '$url'
    #   in sub ain't-taking-non-perl-stuff at script.p6 line 16
    #   in block <unit> at script.p6 line 23

Whoa! The message printed twice. What gives?

It’s actually expected that the refinement in subsets is an inexpensive and relatively simple operation… With that expectation in mind, the parameter binder—which doesn’t know how to generate errors—simply passes its stuff through the slower code path—which does—and it’s that slower code path that runs the where code the second time, triggering our message one more time.

So yes, doing overly complex stuff in subsets is abusive. However, you can throw an exception inside the where to avoid the repetition of the message:

    sub is-perl-site {
        LWP::Simple.get( $^website ).contains: 'Perl 6'
            or die ’This ain't no website containing "Perl 6"!‘;
    }

    ...

    ain't-taking-non-perl-stuff 'http://lolcats.com';
    # This ain't no website containing "Perl 6"!
    #   in sub is-perl-site at z.p6 line 4
    #   in any accepts_type at gen/moar/m-Metamodel.nqp line 3472
    #   in sub ain't-taking-non-perl-stuff at z.p6 line 11
    #   in block <unit> at z.p6 line 18

And don’t forget to check out Subset::Helper and Subset::Common modules.

What About a Light Spanking?

There is one type of abuse cheating with subsets that can get you out of a bind: fiddling with narrowness when it comes to resolution of multi candidates.

For example, let’s say you hate humanity and you wish to change the meaning of the infix + operator on two Ints to do subtraction instead of addition. You, of course, write this:

    multi sub infix:<+> (Int $a, Int $b) { $a$b }

But as you run a sample code, over the sound of your evil laughter…

    Ambiguous call to 'infix:<+>'; these signatures all match:
    :(Int:D \a, Int:D \b --> Int:D)
    :(Int $a, Int $b)
      in block <unit> at z.p6 line 4

… the complier errors out.

You see, core language already has an infix + operator that takes two Ints! When you add one of your own, you create an ambiguity. To resolve this issue, we need to somehow create an Int that the compiler thinks is narrower than an Int, but in reality isn’t. Sounds tough? Not an issue for subsets:

    subset NarrowInt of Int where {True};
    multi sub infix:<+> (NarrowInt $a, NarrowInt $b) { $a$b }

    say 42 + 2; # 40

It worked! We created a subset of Int, so we match all of Ints, just like we wanted. In the refinement, however, we specify a single code block that always returns True, making that refinement always succeed, and making our subset accept all the values a regular Int accepts, while being narrower than a regular Int as far as multi resolution goes.

If you’re wondering why we had to use an explicit block, it’s because the where smartmatches, and the smartmatch against a True produces a warning, because it’s always true, and while that is what we want here, in most code such a construct is a mistake.

But if you’re upset about writing one-too-many characters, here’s neat trick:

    multi sub infix:<+> (Int $a where {True}, Int $b where {True}) { $a - $b }
    say 42 + 2;

You don’t need to create an explicit subset, and can stick a where clause right onto the thing you’re working with to refine just that thing. The type constraint on it will function as the of ... of a subset.

You can also type constraint a variable with a subset and still add a where clause. Or create a subset of a subset of a subset and still add… well, we’re getting carried away.

When they stop calling…

Consider this piece of wonderful code:

    class Thingie {
        multi method stuff ($ where /meows/) { say "Meow meow!"; }
    }

    class SubThingie is Thingie {
        multi method stuff ($ where /foos/) { say "Just foos..."; }
    }

    SubThingie.new.stuff: 'meows and foos'; # Just foos...

You have a class with a multi method. Along with it, you have a subclass of it with another multi method of the same name. Both have a where clause and when you call the method with input that can match either multi, the subclass’s multi gets called. But what do you do if you want to reverse that… you want the parent class’s multi to be called, if both multies matches the input.

The first solution is very simple. Just add a type constraint (we’ll use Str) in the parent class, while leaving it off in the child:

    class Thingie {
        multi method stuff (Str $ where /meows/) { say "Meow meow!"; }
    }

    class SubThingie is Thingie {
        multi method stuff ($ where /foos/) { say "Just foos..."; }
    }

    SubThingie.new.stuff: 'meows and foos'; # Meow meow!

The presence of a type constraint on the method in the parent class makes it narrower than the one in the subclass, so even though the subclass’s method can also accept the input, it’s the parent class that gets to take care of it.

However, what if we wanted the same Str type constraint on both methods? The parent class we’ll leave as is: just a normal Str type constraint. In the kid, however, we’ll use a wider subset of Any (that’s the default if you don’t specify the of, remember?), but in its where clause we’ll smartmatch against Str, to ensure the subset accepts only Strs:

    class Thingie {
        multi method stuff (Str $ where /meows/) { say "Meow meow!"; }
    }

    class SubThingie is Thingie {
        subset WiderStr where { $_ ~~ Str };
        multi method stuff (WiderStr $ where /foos/) { say "Just foos..."; }
    }

    SubThingie.new.stuff: 'meows and foos'; # Meow meow!

The result is the opposite of a cheat we made in the previous section: instead of a subset that matches a type exactly, but is narrower than it, we now created a subset that matches a type exactly, but is wider than it, as far as multi candidate resolution goes. And yes, you can just merge the two where clauses instead of creating a subset, producing:

    multi method stuff ($ where { $_ ~~ Str and $_ ~~ /foos/ }) {
        say "Just foos...";
    }

It’ll work the same.

Conclusion

Subsets are a powerful feature that lets you specify refinements on existing core and custom types. You can smartmatch against a subset to perform a check on a value, or you can use subsets to type-contraint variables, parameters, and return values.

You can use the subset keyword to create a named subset, or you can attach a refinement onto a specific variable or parameter with a where clause. Subsets can also be used to effect alternative narrowness of a parameter, to affect multi candidate resolution order.

Subsets can also be abused to perform very complex operations, but… that’s probably a bad idea.

-Ofun

Day 7 — Set In Your Ways: Perl 6’s Setty and Baggy Types

There’s a relatively common pattern I see with people writing code that counts… say, DNA bases in a string:

my %counts;
%counts{$_}++ for 'AGTCAGTCAGTCTTTCCCAAAAT'.comb;
say %counts<A T G C>; # (7 7 3 6)

Make a Hash. For each thing you want to count, ++ that key in that Hash. So what’s the problem?

Perl 6 actually has specialized types that are more appropriate for this operation; for example, the Bag:

'AGTCAGTCAGTCTTTCCCAAAAT'.comb.Bag<A T G C>.say; # (7 7 3 6)

Let’s talk about these types and all the fancy operators that come with them!

A Note on Unicode

I’ll be using fancy-pants Unicode versions of operators and symbols in this post, because they look purty. However, all of them have what we call “Texas” equivalents you can use instead.

Ready. Set. Go.

The simplest of these types is a Set. It will keep exactly one of each item, so if you have multiple objects that are the same, the duplicates will be discarded:

say set 1, 2, 2, "foo", "a", "a", "a", "a", "b";
# OUTPUT: set(a, foo, b, 1, 2)

As you can see, the result has only one a and only one 2. We can use the , U+2208 ELEMENT OF, set membership operator to check if an item is in a set:

my $mah-peeps = set <babydrop iBakeCake Zoffix viki>;
say 'Weeee \o/' if 'Zoffix'  $mah-peeps;
# OUTPUT: Weeee \o/

The set operators are coercive, so we don’t need to explicitly create a set; they’ll do it for us:

say 'Weeee \o/' if 'Zoffix'  <babydrop iBakeCake Zoffix viki>;
# OUTPUT: Weeee \o/

But pay attention when using allomorphs:

say 'Weeee \o/' if 42  <1 42 72>;
# No output

say 'Weeee \o/' if 42  +«<1 42 72>; # coerce allomorphs to Numeric
# OUTPUT: Weeee \o/

The angle brackets create allomorphs for numerics, so in the first case above, our set contains a bunch of IntStr objects, while the left hand side of the operator has a regular Int, and so the comparison fails. In the second case, we coerce allomorphs to their numeric component with a hyper operator and the test succeeds.

While testing membership is super exciting, we can do more with our sets! How about some intersections?

my $admins = set <Zoffix mst [Coke] lizmat>;
my $live-in-North-America = set <Zoffix [Coke] TimToady huggable>;

my $North-American-admins = $admins  $live-in-North-America;
say $North-American-admins;
# OUTPUT: set(Zoffix, [Coke])

We intersected two sets with the , U+2229 INTERSECTION, intersection operator and received a set that contains only the elements present in both original sets. You can chain these operations too, so membership will be checked in all of the provided sets in the chain:

say <Zoffix lizmat>  <huggable Zoffix>  <TimToady huggable Zoffix>;
# OUTPUT: set(Zoffix)

Another handy operator is the set difference operator, whose Unicode look I find somewhat annoying: No, it’s not a backslash (\), but a U+2216 SET MINUS character (luckily, you can use the much more obvious (-) Texas version).

The usefulness of the operator compensates its shoddy looks:

my @spammers = <spammety@sam.com  spam@in-a-can.com  yum@spam.com>;
my @senders  = <perl6@perl6.org   spammety@sam.com   good@guy.com>;

for keys @senders  @spammers -> $non-spammer {
    say "Message from $non-spammer";
}

# OUTPUT:
# Message from perl6@perl6.org
# Message from good@guy.com

We have two arrays: one contains a list of spammers’ addresses and another contains a list of senders. How to get a list of senders, without any spammers in it? Just use the (fine, fine, the (-)) operator!

We then use the for loop to iterate over the results, and as you can see from the output, all spammers were omitted… But why is keys there?

The reason is Setty and Mixy types are a lot like hashes, in a sense that they have keys and values for those keys. Set types always have True as values, and since we don’t care about iterating over Pair objects in our loop, we use the keys to get just the keys of the set: the email addresses.

However, hash-like semantics aren’t useless on Sets. For example, we can take a slice, and with :k adverb return just the elements that the set contains:

my $meows = set <
    Abyssinian  Aegean  Manx      Siamese  Siberian  Snowshoe
    Sokoke      Sphynx  Suphalak  Thai
>;

say $meows<Sphynx  Raas  Ragamuffin  Thai>:k;
# OUTPUT: (Sphynx Thai)

But what happens if we try to delete an item from a set?

say $meows<Siamese>:delete;
# Cannot call 'DELETE-KEY' on an immutable 'Set'
# in block <unit> at z.p6 line 6

We can’t! The Set type is immutable. However, just like Map type has a mutable version Hash, so does the Set type has a mutable version: the SetHash. There isn’t a cutesy helper sub to create one, so we’ll use the constructor instead:

my $s = SetHash.new: <a a a b c d>;
say $s;
$s<a d>:delete;
say $s;

# SetHash.new(a, c, b, d)
# SetHash.new(c, b)

Voilà! We successfully deleted a slice. So, what other goodies does Santa have in his… bag?

Gag ’em ‘n’ Bag ’em

Related to Sets is another type: a Bag, and yes, it’s also immutable, with the corresponding mutable type being BagHash. We already saw at the start of this article we can use a Bag to count stuff, and just like a Set, a Bag is hash-like, which is why we could view a slice of the four DNA amino acids:

'AGTCAGTCAGTCTTTCCCAAAAT'.comb.Bag<A T G C>.say; # (7 7 3 6)

While a Set has all values set to True, a Bag‘s values are integer weights. If you put two things that are the same into a Bag there’ll be just one key for them, but the value will be 2:

my $recipe = bag 'egg', 'egg', 'cup of milk', 'cup of flour';
say $recipe;
# OUTPUT: bag(cup of flour, egg(2), cup of milk)

And of course, we can use our handy operators to combine bags! Here, we’ll be using , U+228E MULTISET UNION, operator, which looks a lot clearer in its Texas version: (+)

my $pancakes = bag 'egg', 'egg', 'cup of milk', 'cup of flour';
my $omelette = bag 'egg', 'egg',  'egg', 'cup of milk';

my $shopping-bag = $pancakes  $omelette  <gum  chocolate>;
say $shopping-bag;
# bag(gum, cup of flour, egg(5), cup of milk(2), chocolate)

We used two of our Bags along with a 2-item list, which got correctly coerced for us, so we didn’t have to do anything.

A more impressive operator is , U+227C PRECEDES OR EQUAL TO, and it’s mirror , U+227D SUCCEEDS OR EQUAL TO, which tell whether a Baggy on the narrow side of the operator is a subset of the Baggy on the other side; meaning all the objects in the smaller Baggy are present in the larger one and their weights are at most as big.

Here’s a challenge: we have some materials and some stuff we want to build. Problem is, we don’t have enough materials to build all the stuff, so what we want to know is what combinations of stuff we can build. Let’s use some Bags!

my $materials = bag 'wood' xx 300, 'glass' xx 100, 'brick' xx 3000;
my @wanted =
    bag('wood' xx 200, 'glass' xx 50, 'brick' xx 3000) but 'house',
    bag('wood' xx 100, 'glass' xx 50)                  but 'shed',
    bag('wood' xx 50)                                  but 'dog-house';

say 'We can build...';
.put for @wanted.combinations.grep: { $materials  [] |$^stuff-we-want };

# OUTPUT:
# We can build...
#
# house
# shed
# dog-house
# house shed
# house dog-house
# shed dog-house

The $materials is a Bag with our materials. We used xx repetition operator to indicate quantities of each. Then we have a @wanted Array with three Bags in it: that’s the stuff we want to build. We’ve also used used the but operator on them to mix in names for them to override what those bags will .put out as at the end.

Now for the interesting part! We call .combinations on our list of stuff we want, and just as the name suggests, we get all the possible combinations of stuff we can build. Then, we .grep over the result, looking for any combination that takes at most all of the materials we have (that’s the operator). On it’s fatter end, we have our $materials Bag and on its narrower end, we have the operator that adds the bags of each combination of our stuff we want together, except we use it as a metaoperator [⊎], which is the same as putting that operator between each item of $^stuff-we-want. In case you it’s new to you: the $^ twigil on $^stuff-we-want creates an implicit signature on our .grep block and we can name that variable anything we want.

And there we have it! The output of the program shows we can build any combination of stuff, except the one that contains all three items. I guess we just can’t have it all…

…But wait! There’s more!

Mixing it Up

Let’s look back at our recipe code. There’s something not quite perfect about it:

my $recipe = bag 'egg', 'egg', 'cup of milk', 'cup of flour';
say $recipe;
# OUTPUT: bag(cup of flour, egg(2), cup of milk)

What if a recipe calls for half a cup of milk instead of a whole one? How do we represent a quarter of a teaspoon of salt, if Bags can only ever have integer weights?

The answer to that is the Mix type (with the corresponding mutable version, MixHash). Unlike a Bag, a Mix supports all Real weights, including negative weights. Thus, our recipe is best modeled with a Mix.

my $recipe = Mix.new-from-pairs:  'egg'          => 2, 'cup of milk' => ½,
                                  'cup of flour' => ¾, 'salt'        => ¼;
say $recipe;
# mix(salt(0.25), cup of flour(0.75), egg(2), cup of milk(0.5))

Be sure to quote your keys and don’t use colonpair form (:42a, or :a(42)), since those are treated as named arguments. There’s also a mix routine, but it doesn’t take weights and functions just like bag routine, except returning a Mix. And, of course, you can use a .Mix coercer on a hash or a list of pairs.

Less-Than-Awesome creation aside, let’s make something with mixes! Say, you’re an alchemist. You want to make a bunch of awesome potions and you need to know the total amount of ingredients you’ll need. However, you realize that some of the ingredients needed by some reactions are actually produced as a byproduct by other reactions you’re making. So, what’s the most efficient amount of stuff you’ll need? Mixes to the rescue!

my %potions =
    immortality  => (:oxium(6.98), :morphics(123.3),  :unobtainium(2)   ).Mix,
    invisibility => (:forma(9.85), :rubidium(56.3),   :unobtainium(−0.3)).Mix,
    strength     => (:forma(9.15), :rubidium(−30.3),  :kuva(0.3)        ).Mix,
    speed        => (:forma(1.35), :nano-spores(1.3), :kuva(1.3)        ).Mix;

say [] %potions.values;
# OUTPUT: mix(unobtainium(1.7), nano-spores(1.3), morphics(123.3),
#              forma(20.35), oxium(6.98), rubidium(26), kuva(1.6))

For convenience, we set up a Hash, with keys being names of potions and values being Mixes with quantities of ingredients. For reactions that produce one of the ingredients we seek, we’ve used negative weights, indicating the amount produced.

Then, we used the same set addition operator we saw earlier, in it’s meta form: [⊎]. We supply it the .values of our Hash that are our Mixes, and it happily adds up all of our ingredients, which we see in the output.

Look at unobtainium and rubidium: the set operator correctly accounted for the quantities produced by reactions where those ingredients have negative weights!

With immortality potion successfully mixed, all we need to do now is figure out what to do for the next few millennia… How about coding some Perl 6?

Day 6 – Perl 6 Books — the Time is Ripe

One question we occasionally get on the phenomenal #perl6 IRC channel is about Perl 6 books. It turns out, some folks don’t want to learn just from tutorials, blog posts and docs. Last year, we didn’t have any good answers to that question.

A few months ago, there seemed to be a flurry of such questions, and at the same time, rumours about new book projects started to spread.

If I remember correctly, the first rumour was when Laurent Rosenfeld contacted me in June, asking for feedback on a manuscript. He had translated a good 200 pages of the Think Python book to Perl 6. This is primarily a book teaching programming to beginners. Later I was happy to hear that a major publisher has accepted the manuscript. The manuscript is now finished, and under review. So I guess we’ll see e-book and print versions of this book in due time.

Then brian d foy opened a kickstarter to raise funds for a “Learning Perl 6” book. By the time this is published, the kickstarter project should still be open, so I warmly encourage you to support this. Having a book by a prominent member of the Perl 5 community, and author of several good Perl books would surely be a boon for the Perl 6 community.

Before the publication of brian’s project, I’ve been mulling about writing my own Perl 6 book. In the past, I’ve tried that once already, and failed. But I had more success with another book project of mine, so I decided to give it another shot. The working title is Perl 6 by example. Content for the book is published in form of blog posts, and later turned into book chapters.

Later I learned that Ken Youens-Clark had written a book on metagenomics that spends about 150 pages explaining Perl 6. And it’s free, you can download the PDF, epub and mobi versions right away! If you’ve followed the advent calendar, you might have read Ken’s earlier post on Bioinformatics

In summary, we have one book starring Perl 6 content, one in progress with the manuscript in stage “feature complete”, one in the kick-starting phase which you can support, and a fourth being written right now, with parts being pre-published in the form of blog posts.

If you want to keep up-to-date with news on Perl 6 books, you can sign up for the low volume Perl 6 book mailing list (less than one mail per month).

I hope that in next year’s advent calendar, we’ll see four reviews of Perl 6 books.

Day 5 – How to use the docs

…or why (nearly) everything is an object.

Spread out over all the pages of the official documentation of Perl 6, the class Signature is linked to more then 40 times. That shows not only that signatures are everywhere but also demonstrates a peculiarity of Perl 6 that will become more important as the language matures. No really, we are not done yet.

But first let’s have a look at some code.

multi sub trait_mod:<is> (Sub $s, :$foo!) is foo {
    say 'is foo called'
}
sub bar {}
&trait_mod:(&bar, :foo);

Here we define a trait that happens to be of type Sub which Rakudo (re)defines in Sub.pm. So there is actually a class for this build-in type. In fact, there are classes for anything besides classes and grammars. Having a Class for class would be a bit circular and prevent a slang to define its own object system. That you can do with Metamodel::ClassHOW, which is the Class behind class. But back to the trait.

A trait is somewhat macroish in nature as it is applied at compile time to some object its signature refers to. That allows the MMD to pick the candidate defined above when we use it with is foo. (There are a whopping 91 candidates of is defined in Rakudo.) Since it’s called at compile time it has to be called with something that exists at that time. All those classes for Sub, Label and friends provide a nice target for such a call. And yes, you can apply a trait to a label if you call it the right way in a BEGIN phaser. Combined with the power of compile time mixins and the the mighty .wrap you can go wild and improve the tool that is most dear to you — the Perl 6 compiler. If you observed the example carefully you likely spotted that the trait is applied to itself. If you really want to you could have infinite recursion at compile time.

Perl 6 is a dynamic dynamic language. The programs you write are dynamic and you can mend them and bend them to your will, both at compile- and runtime. And so you can do with the compiler. And that leads us to how to use the docs. Since language features are represented as a class, we link heavily to those classes to provide detailed discussions and you are encouraged to follow those links before you continue with the main paragraph the link refers from.

Macros will make good use of all those objects. With any luck next year will show us how.

Day 4 – Quantum Secret Santa

Much has already been written about the relationship between Santa Claus and quantum mechanics.  This makes sense intuitively — Unobservable?  In multiple places at once? We only see the effects? It almost goes without saying that Santa is a macroscopic quantum phenomenon.

Similarly,  the game of secret santa has been analyzed by combinatorists and cryptographers for quite some time.  How many ways can people give gifts to each other? How can Alice and Bob and their friends have a decent protocol for their secret santa party?

But the application of quantum states as a practical solution to secret santa didn’t become evident to me until this holiday season. The situation was this: my family and I are hosting guests from out of town. We need to organize a secret santa gift exchange, but don’t want to impose gift giving or secrecy constraints on people who are coming from the same household. More explicitly:

  1. Several households of people are coming to visit us.
  2. Everyone needs to be assigned to give a gift to someone else.
  3. Everyone needs to be given their assignments ahead of time.
  4. Nobody should be assigned to someone within their household.

Sounds like a job for Perl 6!

Before getting to the solution, let’s go through some background and prerequisites for solving this.

First, quantum superpositions.

Way back in 2000, Damian Conway wrote Quantum::Superpositions for Perl 5. The cool idea here was that instead of dealing with qubits, we could deal with a macroscopic version — variables that have several values at the same time. This idea was then brought into Perl 6 in the form of junctions — logical superpositions of values  — a variable representing several values at once.  Such variables can be treated like a single value, but operators and methods apply to all the values (and can be autothreaded). The routines any, all, one and none turn a list of values into a junction.  Without even reading the documentation or thinking about quantum theory, though, these examples make sense if you just say them out loud:

say so (1, 2, 3).all < 4;  # True
say so (1, 2, 3).any < 3;  # True
say so (1, 2, 3).one < 2;  # True
say so (1, 2, 3).none > 10; # True

As in, “So,  1, 2 and 3 — all of them are less than 4?”

Multiple junctions can be part of an expression, for instance:

say so (1, 2, 3).all < (7, 8, 9).all;    # True
say so (1, 2, 3).all == (4, 5, 6).none;  # True

Think: all of 1, 2, and 3 are less than all of 7, 8, and 9?

By the way, so casts an expression to boolean.

The second prerequisite to solving our secret santa problem is set operations. Unicode characters that serve as set operators are really convenient here.

Basically, the Unicode set operators all work just as you would expect.  Quick — what do think is the output of these statements?

say so (2, 4) ⊂ (2, 4, 6);
say so 2 ∈ (1, 2);
say so 10..20 ⊆ 10..20;

Really, the only tricky thing here is how do you type ⊆, ∈, ⊂ and others on your keyboard?  (Answer: command-control-space on a mac,  control-K + “(” + “_” in vim. Actually, there’s a section of the perl6 documentation about this very topic.). These operators are defined on sets.  But also, using one of these operators on a List will automatically create a set.

The third thing to know about is the Z meta operator — this zips two things together.  The way in which the corresponding elements are combined is determined by a parameter — another operator (which is why it’s a meta operator).

say (1, 2, 3) Z+ (4, 5, 6)  # (5, 7, 9)
say (1, 2, 3) Z=> (4, 5, 6) # (1 => 4 2 => 5 3 => 6)

If Z is given =>, the pair constructor, it’ll make a list of pairs (which can be cast into a hash).

Okay — enough prerequisites.  Let’s write the program already!

my $groups = ( <comet cupid rudolph>, <dancer prancer>, <donner blitzen> );
my @santas = $groups.flat;
my %pairs;

repeat {
 %pairs = @santas Z=> @santas.permutations.pick;
} until %pairs.none.kv ⊆ $groups.any;

Oh, I almost forgot: permutations gives you a list of all permutations of a list.  Also pick returns a random element of a list.

Anyway, the hard part is done!  That clause in the until section works like this: %pairs.none returns a junction of pairs.  Calling kv on that junction makes a junction composed of two-element lists (keys and values of the pairs).  Meanwhile, $groups.any makes a junction of the list of lists. The subset operator, ⊆, then asserts that none of the elements of the left hand side are subsets of any of the elements of the right hand side.  i.e. none of the key-value pairs are subsets of any of the groups. Once again, writing it out in English is pretty similar to how it looks in Perl 6.

To notify everyone, we are going to send an email.  We put everyone’s email addresses into a hash:

my %emails =
   comet   => 'comet213@our.home',
   cupid   => 'cupid99@our.home',
   rudolph => 'rudolph101@our.home',
   dancer  => 'dancer99@reindeer.game',
   prancer => 'prancer1@reindeer.game',
   donner  => 'donner99@reindeer.party',
   blitzen => 'blitzen2@reindeer.party';

Then we can use run to use an external program — sendmail (or postfix, msmtp, or any similar mailer) — to send out the message.

for @santas.sort -> $santa {
    my $p = run '/usr/sbin/sendmail', %emails{$santa}, :in;
    $p.in.say: qq:to/END/;
       From: santa@north.pole
       To: { $santa.tc } <{ %emails{$santa} }>
       Subject: 🎅

       Dear { $santa.tc },

       Please get a gift for { %pairs{$santa}.tc }!
       END
 $p.in.close;
}

Notice that we use .tc to capitalize the name.  This stands for “title case” — a Unicode generalization of upper casing the first letter. For instance, a name like ʣenana (in which the first character is a single Unicode character — a digraph) would be properly title cased as Dzenana, not DZenana.

That’s it for the program — after showing everyone the complete program on github, even the least technical guest was quickly able to understand how it worked.  It ran smoothly and now everyone’s ready for the holidays!

 

Day 3 – Object Hashes

A short .promotional message: You may want to check out my Kickstarter campaign for Learning Perl 6, or the stuff I’m writing at the book’s website.

Perl 6 adds object hashes in which the keys aren’t merely strings. They are values and types put together. That means that the objects can stringify to the same thing but they can be different keys.

First, consider this normal hash construction. I’ll put a bunch of things into the hash then see what I got:

use v6;

my Int    $int     = 4;
my Str    $str     = "4";
my IntStr $int_str = <4>;  # Allomorph

my %hash;
%hash{$int}     = 'Plain old number';
%hash{$str}     = 'String of digits';
%hash{$int_str} = 'Dualvar';

say "There are ", %hash.elems, " elements in the hash";

# this calls the .gist method, sorta like a dumper routine
%hash.say;

The output shows that I only have one element in the hash and it’s the last one that I added:

There are 1 elements in the hash
{4 => Dualvar}

But, I can declare my hash in another way. I can declare it as an object hash by telling it what sort of objects I want it to accept. I can use the Any object which allows it to accept, well, anything:

my %hash{Any}; # accept any sort of object

This program is almost exactly the same but it acts much differently:

use v6;

my Int    $int     = 4;
my Str    $str     = "4";
my IntStr $int_str = <4>;  # Allomorph

my %hash{Any};
%hash{$int}     = 'Plain old number';
%hash{$str}     = 'String of digits';
%hash{$int_str} = 'Dualvar';

say "There are ", %hash.elems, " elements in the hash";

# this calls the .gist method, sorta like a dumper routine
%hash.say;

Now I see that I have three elements in the hash. It looks a bit strange in the .gist form because it appears that it has three keys that are all 4:

There are 3 elements in the hash
{4 => Dualvar, 4 => Plain old number, 4 => String of digits}

I can look at the .perl method that looks a bit more behind the scenes:

%hash.perl.say;

Now I see that there are three different sorts of objects:

There are 3 elements in the hash
(my Any %{Any} = IntStr.new(4, "4") => "Dualvar", 4 => "Plain old number", "4" => "String of dig<4>")

With an object hash, testing for existence is a bit different. It uses the .WHICH method, comparing the keys with the object identity operator ===.

So, I’ll put the same stuff in the hash then make a different object that I assign to $other_int. I check to see if $other_int is in the hash:

use v6;

my Int    $int     = 4;
my IntStr $int_str = <4>;  # Allomorph

my %hash{Any};
%hash{$int}     = 'Plain old number';
%hash{$int_str} = 'Dualvar';

my $other_int = 4;

# what are these things?
say "int: " ~ $int.WHICH;
say "other: " ~ $other_int.WHICH;

# are they the same?
say $int === $other_int ?? 'Same object' !! 'Different object';

# is it in the hash?
say %hash{$other_int}:exists ?? 'Other int exists in hash' !! 'Other int not there';

say %hash{"4"}:exists ?? '"4" exists in hash' !! '"4" not there';
there';

I can see that $int and $other_int look like the same object. However, the key "4" is not there even though it’s the same string as 4:

int: Int|4
other: Int|4
Same object
Other int exists in hash
"4" not there

That can be a bit weird if I wasn’t expecting that.

Consider the angle brackets version of quote words operator, <...>. This form of quote words creates allomorphs. When it sees things that look like numbers, it creates an object such as IntStr that inherits from both number and string sides. This means, though, that as an object hash key it has a very particular form. In this object hash, I make one element with the <...> quoting around the key 4. Then, I test to see if the string "4" is in the hash:

use v6;

my %hash{Any};

%hash = <4 hello>;

say %hash{"4"}:exists ?? 'Exists in hash' !! 'Not there';

And, I see that it’s not in the hash:

Not there

The allomorph version is an IntStr, but the "4" is a Str. They are not the same object, so the latter one is not a key in the hash.

That’s not a big deal if you are expecting that. But, consider a more useful object hash that only allows a certain sort of object. Maybe I want them to all be Date objects. This way, I force people to go through a central way of making keys. If they try to go around that, things don’t work:

my %hash{Date};

%hash{ Date.new(now) } = ( event => 'Something cool', rating => '6 stars' );

my $too_cool_for_your_api = '12-03-2016';

say %hash{ $too_cool_for_your_api };

I get an exception when I try to go around the constraint when I think I know better than the API:

Type check failed in binding to key; expected Date but got Str ("12-03-2016")

Perl 6 lets me force the other programmers to construct the hash keys just the way I need them.

Day 2 – Bioinformatics with Perl 6

Summer of 2016 and I’m preparing to help teach a class on metagenomics with my boss, Dr. Bonnie Hurwitz, at The University of Arizona.  She and I are both alumi of Dr. Lincoln Stein’s lab when he was at Cold Spring Harbor where we worked on Gramene, a comparative genomics platform for plants.  Way back in 1999, Lincoln created an intensive two-week course at CSHL called “Programming for Biologists” to teach everything from Unix to Perl to BLAST and CGI.  I was fortunate to be a teaching assistant several times over the years and came to enjoy helping bench scientists learn computational tools and techniques.  In the fall 2015 debut class of Bonnie’s class, we used Lincoln’s material to teach Perl 5, but I was ready to push into a new language.

Over the years, I’ve played with Python, Lisp, Ruby, Haskell, Prolog, and others.  Python would have been the obvious choice to teach our students, but I felt like I already knew an interpreted, dynamically typed language.  While I admire the type safety of Haskell, I can’t imagine being able to teach command-line Unix, HPC, metagenomic analysis, and Haskell to absolute beginners in one semester.  I decided to spend a month over the summer writing in Perl 6, and it didn’t take long until I was hooked.

There were no print books available and only a handful of online resources like https://docs.perl6.org/http://perl6intro.com/, and https://learnxinyminutes.com/docs/perl6/.  None of these were tailored to beginning scientists, so I started writing my own.  My idea was to teach solutions to common problems in biology using the various styles and strengths of the language.  For each task, I present maybe 2 or sometimes 6 versions, usually getting shorter as I teach more powerful techniques gained by functional programming techniques or object-oriented programming.

As an example, our students are writing their final program for the semester, wrapping up a re-analysis of the skin microbiome (Grice et al 2011).  Over these last few months, they’ve followed a series of detailed protocols to download the raw reads, push them through quality control measures, assemble into “contigs” (contiguous bits of sequence), call putative genes, estimate taxonomy, and annotate gene to determine the functions of the organisms at the various body sites (e.g., armpit vs toe web).  Their last assignment is to annotate the contigs with KEGG categories.

Since KEGG supports itself with yearly subscriptions, among other funding, they don’t make it exactly easy to get all the data our students need.  We used uproc to attach KEGG identifiers, and we want to group those identifiers into categories to find the top 5 functions happening in each student’s subset of samples. I found a handy shell script to use the KEGG API to download KEGG-IDs-to-pathways and pathways-to-categories.  So now we need to link KEGG IDs (e.g., “K01425”) to the pathway category (e.g., “GABAergic synapse”) via the “path:map04727”:

$ head -1 path:map04727.ko
path:map04727 ko:K01425
$ grep map04727 pathway.list
path:map04727 GABAergic synapse

Here’s a script that accomplishes the task:

#!/usr/bin/env perl6

sub MAIN (
    Str  :$pathway-list where *.IO.f = 'pathway.list',
    Str  :$pathway-dir  where *.IO.d = 'pathways',
    Str  :$out-file = 'kegg-cats.txt',
    Bool :$force    = False,
) {
    if $out-file.IO.f && $force == False {
        my $overwrite = prompt "'$out-file' exists. Overwrite [yN]? ";
        exit unless $overwrite.lc.starts-with('y');
    }

    my $out-fh = open $out-file, :w;
    my %map    = $pathway-list.IO.lines.map(*.split("\t")).flat;

    for dir($pathway-dir).grep(/'.ko'$/).kv -> $i, $ko {
        printf "%3d: %s\n", $i + 1, $ko.basename;

        for $ko.IO.lines.map(*.split("\t")) -> ($map-id, $ko-id) {
            my $cat     = %map{ $map-id } or next;
            my $kegg-id = $ko-id.subst(/^'ko:'/, '');

            if $kegg-id.match(/^ K \d ** 5 $/) {
                $out-fh.put(join "\t", $kegg-id, $cat);
            }
        }
    }

    put "Done, see '$out-file'.";
}

I’d like to break this down to explain all the lovely goodies.  First and thing-that-completely-sold-me-on-Perl-6 is the special MAIN subroutine and all the goodness packed into signatures.  Named arguments, e.g., “–pathway-list,” can defined by creating Pairs, which is as simple as putting a “:” in front of the variable that will hold the argument’s value.  I can assign a default value with “=”, constrain the value with a Type like “Str” or “Bool,” and even add arbitrary conditions like checking if the argument is an existing file (“*.IO.f”) or directory (“*.IO.d”).  If I see myself reusing those same checks (e.g., I take multiple “file/dir” arguments), I can easily create my own “subset”:

subset File of Str where *.IO.f;
sub MAIN (File :$file1, File :$file2) { ... }

I like to use reasonable defaults for my arguments, and here I wanted to show how I could check if the output file already exists and how to ask the user via “prompt” if they wish to overwrite it.  In Perl 5, I would have written:

exit unless lc($overwrite) =~ /^y/;

A direct translation to Perl 6 would use “.lc” (lowercase) as a Str method and using the ~~ “smart match” operator:

exit unless $overwrite.lc ~~ /^y/;

But here I wanted to show a more Python-ish use of “starts-with” so as to avoid freaking out beginners with regular expressions.  (I like that Perl has a long history of borrowing/stealing the best features of other languages!)

I am very happy with the new “open” routine as the Perl 5 way always seems backwards:

open my $fh, '<', $file; # Perl 5 open $file for read
my $fh = open $file;     # Perl 6 open $file for read

Here, I need to open the output file for writing, so I pass the “:w” (writable) flag which is just a short-hand for the Pair w => True.

The next line is an extremely condensed way to read a tab-delimited file of two columns directly into a hash.  Here is what my “pathway.list” file looks like:

$ head -3 pathway.list
path:map00010 Glycolysis / Gluconeogenesis
path:map00020 Citrate cycle (TCA cycle)
path:map00030 Pentose phosphate pathway

From that, I want a list of the “path:map” string to the category on the right.  So, let’s use the REPL (the second thing that totally sold me on Perl 6) to see how this is built:

$ head -3 pathway.list > test
$ perl6
To exit type 'exit' or '^D'
> 'test'.IO.lines
(path:map00010 Glycolysis / Gluconeogenesis path:map00020 Citrate cycle (TCA cycle) path:map00030 Pentose phosphate pathway)
> 'test'.IO.lines.map(*.split("\t").join('=')).join(', ')
path:map00010=Glycolysis / Gluconeogenesis, path:map00020=Citrate cycle (TCA cycle), path:map00030=Pentose phosphate pathway

File I/O is very easy (IMHO) — I can coerce/cast a variable with “.IO” and then call “lines” (or “words” or even “comb” if I wanted letters).  Here I want to process each line, using our trusty old “map” (from functional programming) to apply the “split” function to the “*” (Whatever) to get a list which, for visual purposes, I join on “=”.  The result of the lines/map is itself a list which I joined on commas so you can see the two lists together.

Something that is completely different between Perl 5 and 6 is how they handle lists of lists.  Perl 5 would flatten them:

  DB x ((1,2), (3,4))
0 1
1 2
2 3
3 4
 DB x scalar ((1,2), (3,4))
0 4

But Perl 6 allows nesting:

> ((1,2), (3,4))
((1 2) (3 4))
> ((1,2), (3,4)).elems
2

But I can’t create a hash from a list of lists, only from a list of pairs:

> my %h = ((1,2), (3,4))
{1 2 => (3 4)}
> my %h = 1 => 2, 3 => 4
{1 => 2, 3 => 4}

I need to flatten my list-of-lists:

> my %h = ((1,2), (3,4)).flat
{1 => 2, 3 => 4}

And that is how I get my hash-from-a-tab-file:

> my %h = 'test'.IO.lines.map(*.split("\t")).flat
{path:map00010 => Glycolysis / Gluconeogenesis, path:map00020 => Citrate cycle (TCA cycle), path:map00030 => Pentose phosphate pathway}

There are a few things I’d like to explain in this line:

for dir($pathway-dir).grep(/'.ko'$/).kv -> $i, $ko

The “dir” routine gives you a directory listing, and, since my type constraint already checked that the variable is an existing directory, I can rest assured that this will work.  The result of “dir” is a list of IO::Path objects, and I can “grep” (“filter” in some other languages) for those that smart-match to the pattern of the string “.ko” anchored to the end (“$”).  (I could have also used the Python-like “ends-with” method.)

The result of the dir/grep call is a list, and as I wrote in another post, you can call call “kv” (as well as “pairs“) on any list to get both the position and value of each member:

> ("foo", "bar").kv
(0 foo 1 bar)
> ("foo", "bar").pairs
(0 => foo 1 => bar)

The other bit of awesomeness is that Perl 6 allows you to consume a variable number of elements of  a list with optional defaults if the list is not evenly divisible by the number of elements you’ve requested:

> for 1..4 -> $i { $i.say }
1
2
3
4
> for 1..4 -> $i, $j { put "$i, $j" }
1, 2
3, 4
> for 1..4 -> $i, $j="NA", $k="NA" { put "$i, $j, $k" }
1, 2, 3
4, NA, NA

Heck, you don’t even have to declare them as we have implicit variables:

> for 1..4 { put "$^a, $^b" }
1, 2
3, 4

So then I have a (zero-offset which is why I add 1) file number and name which I can use to print my progress using “printf.”  In Perl 5, I would have to establish my counter $i before the loop (which means it would continue to be visible after the loop) and be sure to increment the counter while understanding the nuances of ++$i vs $i++:

# Perl 5
my $i = 0;
for my $word (qw[foo bar]) {
    printf "%3d: %s\n", ++$i, $word;
}

While trying similar things in Haskell, I learned to zip (Z) an infinite list of integers (Haskell, like Perl 6, is lazy!) with my list of interest like so:

> for 1..* Z "foo", "bar" -> ($i, $word) { put "$i = $word" }
1 = foo
2 = bar

OK, that was a long tangent, so let’s return to the program.  We left off with this statement to find all the pathway/KEGG ID files:

for dir($pathway-dir).grep(/'.ko'$/).kv -> $i, $ko

As before, I use “.IO.lines” to read each mapping file, also running each line through a similar “split” on tabs to break the lines into the mapping ID and KO ID:

for $ko.IO.lines.map(*.split("\t")) -> ($map-id, $ko-id)

The long-hand way to do this would be:

for $ko.IO.lines -> $line {
    my ($map-id, $ko-id) = $line.split("\t");
}

As in Perl 5, “next” and “last” are still part of control flow, and I skip over mapping IDs that I didn’t find in the mapping file:

my $cat = %map{ $map-id } or next;

The KO ID looks like “ko:K00001”, so I need to remove the leading “ko:” which is easily accomplished with the “subst” (substitute) operation:

my $kegg-id = $ko-id.subst(/^'ko:'/, '');

In Perl 5 I would have done this, which I feel is unquestionably more opaque:

(my $kegg_id = $ko_id) =~ s/^ko://;

It’s worth noting that in Perl 5 I might also have made the decision to reuse and mutate the $ko_id like so:

$ko_id =~ s/^ko://;

But in Perl 6, the my $ko-id variable is not actually variable — by default it is read-only.  In the following example, you see that “subst” returns a new string with the substitutions requested, but the string itself remains unchanged:

> for "foo", "bar" -> $s { put $s.subst(/<[aeiou]>/, 'X', :g); put $s }
fXX
foo
bXr
bar

If I try to call the mutator method, I get an error:

> for "foo", "bar" -> $s { $s.subst-mutate(/<[aeiou]>/, 'X', :g); put $s }
Cannot resolve caller subst-mutate(Str: Regex, Str, :g); the following candidates
match the type but require mutable arguments:
 (Cool:D $self is rw: |c is raw)
 (Str:D $self is rw: $matcher, $replacement, :ii(:$samecase), :ss(:$samespace), :mm(:$samemark), *%options)
 in block  at  line 1

I have to explicitly say the variable is a copy:

> for "foo", "bar" -> $s is copy { $s.subst-mutate(/<[aeiou]>/, 'X', :g); put $s }
fXX
bXr

All this is a nod to the very good influence of purely functional programming languages where data is immutable.  It’s not nice to always be constrained by this idea, but here the application, I believe, enforces a very good programming practice.

Some of the KEGG IDs might be “KO” (KEGG Orthology), so I want to ensure that I’m only dealing with strings that look like “K00001” — a capital “K” followed by five digits:

if $kegg-id.match(/^ K \d ** 5 $/) {

It’s also possible to write that with the smart-match operator.  Both will return Match if successful:

> 'K00001' ~~ /^ K \d ** 5 $/
「K00001」
> say ('K00001' ~~ /^ K \d ** 5 $/).WHAT
(Match)
> my $m = 'K00001' ~~ /^ K \d ** 5 $/
「K00001」
> dd $m
Match $m = Match.new(ast => Any, list => (), hash => Map.new(()), orig => "K00001", to => 6, from => 0)

The ecosystem of native Perl 6modules currently includes BioInfo and BioPerl6.  What’s amazing, though, is that you can use Inline::Perl5 to bring in Perl 5’s BioPerl:

$ cat seqs.fa
>GON5MYK01BC2ZU
CTGTCTGTAACCTCTGCCAGCATGCAGGCGCGTGCTGCGTACCTGAACGAAGGTTGGAAC
TGCATGAACTTGGTGAAGAACATCACCAAGCAAGACCCGCTAGAGTTTGTCGCTAACCGC
CTCACTAGCTATTGGCAGCGCGTAGCCCAGCGTCGCGCTATTCGCCACCACTATCGGCAT
CTACAACGAAAATGTT
>GON5MYK01AXF84
CTTGAGCAAGACCTGCGCCGACTTGTCCTGTTTTCAACTAACTTAGCACCTACGTTACGA
ATAAATCTATCAAATTGTGGTGTGCCAAAAATATCTGATATGCTTGGTCTTTCTGTTTGT
TCTGTAACTTTATCTGTAGGTTTTTC
>GON5MYK01DJ0V1
CATTACTGAAATCTGTCCAGTATTCCACATACAAGAAAGGTCTTGTATCGGAGCTGTTGT
CTACACCTTTAATAGTAAAGTGACGTGTCAATATCTGAACCAGTGATCCAACCTTGTGCT
TATGTCTGACTTCATCATCAAGTGTTAGTGTCGTG
>GON5MYK01DR4LG
AGAAGTAAAAATCAGCAGAGACTTCGACATCGAAAGATTAATTGGTCAAGATATTACAGC
TTTAACATCTCTATTCGATCAACAAGTCATTAGATAGAGACGAATTTAGAGATATTTTAG
TTCAAGGTG
$ cat inline.p6
#!/usr/bin/env perl6

# This is using p5 BioPerl Bio::SeqIO
use Bio::SeqIO:from<Perl5>;

my $file = @*ARGS.shift;

# Note: left side needs quotes; keys are not automaically strings in p6
my $in = Bio::SeqIO.new('-format' => 'fasta', '-file' => $file);

my $ct = 0;
while $in.next_seq -> $record {
    $ct++;
    say $record.display_id;
}

say "Count: $ct";
$ ./inline.p6 seqs.fa
GON5MYK01BC2ZU
GON5MYK01AXF84
GON5MYK01DJ0V1
GON5MYK01DR4LG
Count: 4

Like Perl 5, version 6 still (IMO) makes easy things easy and hard things possible, and I hope this article makes you want to use Perl 6 today.  I will continue to add chapters and examples to my book and Github repo in the hopes that people will steal as much as they need to get started.

Happy Hacking to one and all!