If you create a class:
class Foo { }
the class will inherit all of its methods from Any
, and then Mu
.
I want to create a class that doesn't inherit from any other class: it should contain a single FALLBACK
method that should catch all method calls to instances of that object.
I've looked through the MetaModel
code, but there does not seem to be a simple way to achieve this goal. All suggestions are welcome!
UPDATE: I decided to go the intercept any method call way as described by Jonathan Worthington. This resulted in two new Perl 6 modules on CPAN: InterceptAllMethods and Object::Trampoline.
This is possible, although you're likely to run into practical problems that will require further effort. Calling construction logic is a good example already pointed out in a comment. Further to that, everything is expected to successfully type check against Mu
; such checks are elided in most places as an optimization, but not others, and so you can expect to run into assorted type check failures.
With that aside, here's how to do it. First, create a module which exports a new meta-type for class
.
class RootHOW is Metamodel::ClassHOW {
method has_default_parent_type(|) { False }
}
package EXPORTHOW {
constant class = RootHOW;
}
The metamodel has to somehow be used to set up the Mu
type in the first place, and so here we (ab)use a mechanism that ordinarily means "no, there's no default parent type yet because we didn't bootstrap our object model that far". Stick this into a module, say called Parentless
, and then it's possible to do this:
use Parentless;
class NotAMu {
method FALLBACK($name, |c) {
say "called $name with {c.perl}"
}
}
NotAMu.new
Which outputs:
called new with \()
If your goal is simply to intercept every method dispatch, there's a far less disruptive way that doesn't mess with the type system. For the moment it needs a custom metaclass that disables method cache publication:
class InterceptHOW is Metamodel::ClassHOW {
method publish_method_cache(|) { }
}
package EXPORTHOW {
constant class = InterceptHOW;
}
You can then write:
use InterceptAllTheMethods;
class InterceptThemAll {
method ^find_method(Mu $obj, Str $name) {
return -> | { say "calling $name" }
}
}
InterceptThemAll.new
Note that unlike FALLBACK
, here you return a code object that will then be invoked. You can write this find_method
implementation in the metaclass too, which may be a better factoring; it's hard to say without knowing the problem at hand.
This approach will cause no type-check related issues, let you intercept every method dispatch, and it's easy enough to look up things like bless
and just delegate those to the Mu
implementation.