Perl 6 Lies – What Might Bite the Newly Arrived

Don’t misunderstand; Perl 6 is wonderful and I am having the best time playing with it – getting used to it beauty and strangeness. This article is to document some unexpected gotchas that got me along the way.

Optional signature parameters are sometimes not optional

One of the nice things you can do with Perl is create subroutines and class methods with parameter signatures that can take care of a lot of tedious coding for you. For example, suppose you want to create a method that changes a shoe in your database table. Your method signature might look like this:

class Shoe {

method update(Int :$id!      where { $id > 0 },
              Int :$color-id where { $color-id > 0 },
              Str :$name! ) {

... method code here ...
}

These are named parameters here, where you could call $shoe.update( id => 23, color-id => 8, name => ‘Hush Puppy’);

Parameters with a “!” are required. Named parameters lacking a “!” are optional, or you could put a “?” there. The “where” clause makes sure that whatever is entered will be constrained by the where clause.

Now, we have color-id being optional. However, if you leave it out, your program will bomb out with the following worthless error message:

Invocant requires an instance of type Int, but a type object was passed. Did you forget a .new?

This message goes away if you include the color-id parameter, however. So, this optional parameter actually is not optional.

But if you ask the Perl guys, they’ll say it is optional. It’s just that when using a “where” clause, a bit more is going on. If we didn’t have that where clause on color-id, it wouldn’t matter.

The issue is, Perl wants to use these where clauses in the signature to make sure that parameters are always constrained and typed correctly — and this includes parameters other than the one you’re checking. That is, your color-id parameter can also check stuff with your id or name parameters as well on invocation. So they have to bind early on, if we want to have that extra bit of wonderful functionality and consistency.

Normally you won’t have to worry about this. The only time it matters is when you’re using an optional parameter that includes a “where” clause in the signature. When you do that, the fix is easy, and makes good sense. In the case above, the color-id line would be changed to the following:

Int :$color-id where { not defined $color-id or $color-id > 0 }

The trick is just remembering: when using an optional parameter with a where clause, make sure the where clause will evaluate to true for the constraint — because that constraint will be checked even when the parameter is marked optional. And the documentation has been updated to make this somewhat more clear.

Using submethod BUILD and using attributes with class inheritance

This is very much like the same problem just described, when optional parameters need to be checked for existence when using the “where” clause in the signature.

Whenever you use a submethod BUILD in a class, you have full control over attributes that are assigned to that class. This also means that you have the responsibility of assigning them.

A great Perl shortcut is to have attributes automatically assigned by specifying them in the signature like this:

submethod BUILD(PosInt :$!id, :$!name, :$!address) {}

This lets attributes be set upon the .new invocation, just as normally happens, which is very handy. However, if you do something like use a custom type, like PosInt above, which expects integers greater than 0, you will get an error about trying to set $!id, even when you didn’t set it in .new(). It will complain about not meeting its type constraints, even though you didn’t set it to anything.

So, just like signatures using where clauses, if your variable is going to be undefined, you need to handle that in the body of the BUILD function and not in the signature — otherwise the auto-whatever assignment system will set the value to Any — which is not a PosInt. 😉 So don’t let that bit do the nice auto-assign stuff.

Once can happen twice, or thrice or more!

Perl has long been known for its powerful and creative control structures. One of my favorite new ones in Perl6 is the “once” control structure.

What’s neat is that it takes care of something we all often have to deal with manually. When you’re doing something in a loop, sometimes you’ll want to do things only once inside it, and after that once, though the loop continues, you won’t do that one-time thing again.

until $database.failure {
    once { say "Starting...";
           $log.writesys: "Starting..."; }

    ...all my normal stuff...
}

This will loop and do your bidding until $database.failure returns True (or whatever). However, the code in the “once” block will only be executed one time, no matter how many times the loop happens.

This very, very handy. But Perl 6 is also very multi-threaddy. So you cannot count on “once” to do something just once if you’re using multiple threads for your task. The variable state stuff just isn’t there to cope with such things.

The documentation for once is also updated with this information now, and yet here is another warning. 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *