Overriding 'can'

Ovid on 2005-11-14T00:13:03

How annoying. I've been working on Model::World and I have agents working properly. However, this comes back to the problem of verbs (actions). What happens when I call this:

$agent->put($box, $ring);

Agent was originally a subclass of Thing but that meant Agent inherited all the methods. The code was trying to put a box in the agent instead of the ring in the box. Now Agent delegates certain methods to Things to take advantage of a "hasa" relationship (you're the agent and you have a thing which represents you), but now I have a relatively naive AUTOLOAD method (I'll cache these methods in the future):

sub AUTOLOAD {
    my ($proto, $thing, @args) = @_;
    my $pkg = ref $proto || $proto;
    $AUTOLOAD =~ s/${pkg}:://;
    my $method = $AUTOLOAD;
    if ($thing->can($method)) {
        $thing->$method(@args);
        return $proto;
    }
    return;
}

This presents a problem, though. The agent "can" put things, but only in the context of a given thing. Thus, calling $agent->can('put'); doesn't make a lot of sense. For example, you cannot shatter things but if you make a "mirror" class and provide a shatter() method, you can shatter the mirror. However, you can't shatter a box, even though the agent can try to shatter it. Thus, there are two different types of "can". Whether an agent "can" attempt an action and whether or not a Thing "can" receive the action. In comes introspection.

package Model::World::Thing::Mirror;
use base 'Model::World::Thing';

sub shatter : Verb { ... }

The World has a vocabulary. If all "actionable" methods have a Verb attribute, I can add that verb to the vocabulary. Then the agent's can() method looks like this:

sub can {
    my ($self, $action) = @_;
    if ( Vocabulary->has_word( verb => $action ) ) {
        my $sub = sub {
            my ($proto, $thing, @args) = @_;
            if ($thing->can($action)) {
                $thing->$action(@args);
                return $proto;
            }
        };
        no strict 'refs';
        *$action = $sub;
        return $sub;
    }
    return;
}

This allows us to cleanly separate which messages an agent can send to objects and which methods the object can receive (though I'll need to refactor this for the AUTOLOAD method).

Time for me to start rereading the Attribute::Handlers docs.


I thought...

cog on 2005-11-14T00:21:49

How annoying. I've been working on Model::World and

You know, for a second there I thought you were going to say and there already was one :-)

Re:I thought...

Ovid on 2005-11-14T01:14:32

That would have been phenomenally annoying :)

I had originally looked at Games::3D because that has so much of what I want, but it's designed completely differently and really doesn't support what I need. Bummer.