Re: goal_expansion

Anne Ogborn <[email protected]>
Newsgroups gmane.comp.ai.prolog.swi
Message-ID <[email protected]>
Math free explanation of meta-predicates

Meta predicates are fun!

Meta predicates define information about a predicate. For example, suppose you're making a game
with wizards and spells. Each spell works differently. A player might have a list of spells they can use.
And this list might as well be the actual predicate names for the different spells.
so

fireball(WizIn, WizOut)  
modifies WizIn and binds WizOut to the new state (perhaps with less health or some such)
and 
web_of_stickyness(WizIn, WizOut) 
decreases movement to zero

So Wizard1 has some complicated structure that includes a list of the spells they know
[fireball, web_of_stickyness]  and all is grand. To cast a spell is as easy as getting a member of the list
and doing call(Spell, Target, TargetOut).

Now we want to have different kind of wizards, maybe Necromancers who can use fireballs and Warlocks
who can use web_of_stickyness (and each can use other stuff, maybe glimmering_light can be used by both).  So we'd like to have information about the spell - which means we
want information about the predicates fireball/2 and web_of_stickyness/2

enter meta predicates. We define a fact 

:- discontiguous can_use/2.
can_use(necromancer, fireball).
fireball(WIn, WOut) :- 
         ... bunch of code.
can_use(warlock, web_of_stickyness).
web_of_stickyness(WIn, WOut) :-
        .... bunch of code
can_use(warlock, glimmering_light).
can_use(necromancer, glimmering_light).
glimmering_light(WIn, WOut) :-
      ... bunch of code.



This is grand except we find out we've got a lot of these spells that most anybody can use, so
there's a long list of these can_use/2 facts before each spell, and 
and pretty soon we start adding extensions. Maybe there are spells that you can 'pick up' but not use,
so there's now a bunch of can_use and a bunch of can_pick_up and the whole things becoming unweildy.
So we make a predicate player_can that takes an option list like structure, 

player_can(fireball, [necromancer([pick_up, use]), warlock([pick_up])]).

But now we've got to duplicate complicated code to parse the option list in each of the actual cast_spell,
pick_up_spell, and so on.  Our little fact system was happier that way.

So how about if, when we run into player_can, we expand the option list to the can_use and can_pick_up
right there on the spot?
We have to make player_can a rule, and then call it during code loading. That's what declarations are for.

player_can(Spell, Options) :-
       ... mass of code that asserts the can_use and can_pick_up facts

and then just before the spell we say 

:- player_can(fireball, [necromancer([pick_up, use]), warlock([pick_up])]).
fireball(WIn, WOut) :-
         ... fireball code

so, player_can is a meta predicate.

Now this idea of meta_predicates is particularly powerful when combined with goal_expansion/2. In your case, you can define a meta_predicate that lets you
'mark' the predicates that need your special transformation, then have goal_expansion/2
fix them up for you. The variations on that are limited only by your imagination.

There's a bunch of them in the SWI-Prolog libraries.  

One common form is to mark one of the arguments as some structure with a certain form,
like an option list. Such a list might have items that have predicate functors in them.
When those eventually get called, they'll need to resolve in the module where the
option list was defined, not in the module where they're being called. a term_expansion
rule (term_expansion's about like goal_expansion) can do this.

More fun reading:

http://www.swi-prolog.org/pldoc/doc_for?object=section(%27sec:metapred%27)
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.