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)