Perl6 Multi Method Signatures : Self Introspection
I am a big fan of Perl6. One of it's more powerful features are multi subs (and methods) that allow you to handle calls in different ways based on the signature of the call.
Whilst a number of languages have the ability to do multi dispatch Perl6 not only can perform dispatch on the number of arguments or their types it can also choose which code path to follow based on the arguments values. For instance there's the classic Factorial as a recursive multi sub.
multi sub fac(0) { 1 }
multi sub fac(UInt $f where $f > 0) { $f * fac($f-1) }
Here we define two code paths. One where you call the sub with 0 and another where you call it with an unsigned integer greater than 0. That's great but what if you have an object and want to process method calls differently based on the objects field values?
For example you have the following method:
method retrieve-archived-data() {
# This won't work if our object has not be archived yet.
# Fail this call
fail "Can't Retrieve Data for an object that's never been archived"
unless $.archived;
# Code here that gets archived data from some source.
# It takes a while and the above check was added because it used to take a
# while and then fail.
}
So at some point this method would take a lot of time looking for the archived data and then fail if it had never been archived. So our intrepid developer put a check in at the top the script to avoid that. The problem with that sort of thing is it soon mounts up. It'd be nice to be able to make it a multi method but how? The $ variable isn't in the signature.
Simple. We add it!
Normally when you call an object method the object you call it on is avaliable in the method as self
or $
. But you can also give it a different name to use with the method as well for example:
method set-name( $pet : $name ) {
$pet!name = $name
}
Note that we separate the object name and the rest of the arguments with a :
instead of a ,
as normal. Like other arguments we can add a where
clause between the name and the seperator. With this knowledge we can rewrite our method:
multi method retrieve-archived-data( $ where ! $.archived: ) {
# This won't work if our object has not be archived yet.
# Fail this call
fail "Can't Retrieve Data for an object that's never been archived";
}
multi method retrieve-archived-data() {
# Code here that gets archived data from some source.
}
Note the trailing :
if you miss that out it isn't going to work.
In this way we can remove a lot of the special cases that often pepper method that have been around for a while. Keeping our code lean and easy to read.
I hope you found that interesting. I'm hoping to write some more posts here about Perl6 and why I think that it's well worth some of your time.
Note
I've updated the second example to remove the redundant second check. I should also mention that $
is the anonymous scalar variable (when it's used in the signature). When you are in a method $.foo
is another way of saying self.foo
.
You can, if you want, name a variable to reference inself of self
eg:
method named-access( $name: ) {
$name.field;
}
In this method $name.field
, self.field
and $.field
will all reference the same object.
Sorry for any confusion.
Top comments (7)
Sorry, Simon, I don't seem to be able to parse this:
Do you mean that you are creating
$.archived
and that the fact that is followed by:
defines it?I shall expand on that bit to explain it better.
Ok. I think that's got it.
Sorry again, Simon. The trailing colon separates (please check out the typo) the declaration from what?
I may have to work on the wording some more.
But you're separating the object from the other method arguments.
Thanks!