Jul 13
.WALK This Way
I will be putting on the first Dallas.p6m talk about upcoming Perl 6 features. This posting will discuss one of the introspection tools: .WALK.
Note: Any code posted below is for demonstrational purposes. It is left as an exercise for the reader to polish for any compiler or interpreter.
WALK is a function defined on Perl 6 objects that will walk the multimethod dispatch and return the appropriate matching candidates. It follows the entire multimethod dispatch, regardless of whether a version has been overloaded by the child, applying various match/non-match functions and returns the results. Let’s start off with an example:
module A {
method baz { ... };
method bar { ... };
};
module B is A {
method baz { ... };
method icanhaz { ... };
};
module C is B {
};
module D is A {
method ucanhaz { ... };
};
my @candidates;
my C $c .= new;
@candidates = $c.WALK(:name(baz));
@candidates = $c.WALK(:name(icanhaz));
@candidates = $c.WALK(:include(B));
So basically what I did there was set up a cheezy object hierarchy and did a few WALKs over it. Let’s go over them and discuss what they did.
I should note that it is not obvious or clear from the Specs what a few of these options truely entail. For example, unless you are intimiately familiar with dispatch, the various dispatch method orders are not well defined. I keenly opted to skip them.
I should also note that there is very little information in the Spec (S12) pertaining to .WALK. After having written this post, I will obviously need to present a few questions to the mailinglist.
The first call returns < C B A > because they all have the baz method. The second call only returns B because it is the only one of < C B A > that contains the icanhaz sub. The third call returns < A C > because A was omitted from the results. However, there is a slight issue I wanted to raise with the third call. It’s not clear from the spec that :name is a required parameter (:name!). In all likelihood, the :name parameter is required and this call will result in a compile/runtime error.
There are a multitude of parameters that guide the tree traversal:
- :canonical for canonical dispatch order
- :ascendant for most-derived first, like destruction order
- :descendant for least-derived first, like construction order
- :preorder for like Perl 5 dispatch
- :breadth for like multi dispatch
There are a few other parameters that help with filtering classes/modules/methods along the hierarchy as WALK traverses the multidispatch candidates:
- :super to only include immediate parent classes
- :name
to only include classes containing named method declaration - :omit(Selector) to only include classes that don’t match selector
- :include(Selector) to only include classes that match selector
Another thing that the Spec does not discuss is whether subtrees are pruned out from the search or if they are all smashed onto a flat search space and only matches are returned.
So astute readers are wondering what the Selector parameter can be for the :omit and :include parameters. Let me show you an example taken straight from the spectest:
my @cands = $x.WALK(:name< m >, :include(regex { < [CDE] > }), :omit({ .^can('n') }));
How awesome is that?! You can specify a regex or use the .^can attribute. In perl5, the .can attribute returns a single Code reference. The new .^can attribute returns a .WALK iterator, which can be used with either :include, :omit, or all by itself.
This is a really cool way of letting me explore my objects more. To those of use who have wanted to do similar things with other languages, this feature will greatly assist inspecting foreign code for it’s capabilities (and obviously other things). I usually use introspection to find out if a plugin or if a class in a library has a certain capability (think versioning your classes). So to round out this discussion, I wanted to briefly mention what a few other languages are capable of that is similar.
Java has fairly decent introspection capabilities, referred to as Reflection. You can sift through the constructors, the declared methods, declaring class (if inherited), class fields, and even invoke methods. There are no built in techniques to automatically narrow the search space (defaults to the current Class object).
Python has very limited introspection capabilities. Python’s method is significantly more arcane than everywhere else, unless you like __method__() calls. Python allows you to get attributes on a class/object by using the __getattr__() function. I had trouble digging up a complete list of string literal attribute names, so buyer beware. Therefore, I should note that my comparison to Python is probably incomplete.
C++ has no such facilities. In order to accomplish this, you will have to conjur up clever hacks like this macro that is used to create a database of classes. Xlibs provides some legacy functionality similar to this that can be used for classes and functions, but is generally pretty limited. The closest thing to invoking one of the returns from .WALK in C++ is going to be a hand-rolled combination of boost::function and boost::functional.
Ruby has a way to keep track of known object instantiations, ObjectSpace, and then pull out methods (private, protected, public, and singleton) and instance data. This is done through the basic Object class. It can even do something similar to perl5’s .can (respond_to?). Unfortunately, there are also no built in techniques to automatically narrow the search space.
The ability to guide the search of multimethods built into the language is an advantage not found elsewhere. Keep in mind, I didn’t feel like researching all 10 million languages out there to see if this is a truly original feature.
This is getting a bit long to really be considered a lightning talk, so I will end it here. If anyone has any corrections, suggestions, or comments, feel free to use the comment feature.
tags: dallas.p6m, perl, perl6 expo
2 comments
2 Comments so far
Leave a comment
[...] you may already know from the Iron Man Feed, s1n did a talk on .WALK, which is a selector based system for introspecting the methods of a class. One really interesting [...]
[...] are a few corrections that were needed from my .WALK talk yesterday. Having the Rakudo pumpking sitting next to you makes answering questions much [...]