Transform *arbitrary terms* in a Prolog program at load

I’m looking for a way to transform arbitrary terms in a prolog program at load from one thing to another. In my example below, I’d like the term testy(X) to transform to testz(X) even if it isn’t a top level rule head or goal in the body of a rule. I tried using term_expansion/2 and goal_expansion/2 but I don’t think these are designed for this case. Is there a way to do it?

% Expansion.pl:
term_expansion(testy(X), [testz(X)]).
goal_expansion(testy(X), testz(X)).

% Test.pl
foobar([testy(X)]).

Now from the top level:

?- consult("Expansion.pl").
true.

?- consult("Test.pl").
true

% wanted: foobar([testz(X)]).
? listing(foobar).
foobar([testy(X)]).

true.

What exactly are you doing though? Why is the testy inside a list, inside the argument of a predicate definition? Is there any chance that you can flatten out the whole thing somehow?


You need term_expansion and goal_expansion because they make it easier to deal with the program itself. But in your case, what you want to transform is not part of the program structure, it is just data? You could instead expand your foobar, and just replace testy with testz. Here is the file:

$ cat testy.pl 
term_expansion(foobar(X), foobar(Y)) :-
    maplist(testy_testz, X, Y).

testy_testz(testy(X), testz(X)).

foobar([testy(X)]).

And here is what I see when I consult it:

?- [testy].
Warning: .../testy.pl:6:
Warning:    Singleton variables: [X]
true.

?- listing(foobar).
foobar([testz(X)]).

true.

But without understanding why you need this, I have no idea if this is good advice.

I agree with @Boris. A fully recursive map is fairly easy to define. Using the new rules we get this code (which I like better than the classical Prolog way :slight_smile: ).

map(testy(X), To) =>
    To = testz(X).
map(Compound, To), compound(Compound) =>
    mapargs(map, Compound, To).
map(In, Out) =>
    Out = In.

Thanks @Boris and @Jan. I didn’t think about going at it from a “transform the top level program” point of view, I was more thinking about ways to match and transform the innermost predicate (i.e. testz/1). Good idea, I think that might work for my scenario. I’ll have to think about it more.

@Boris, I avoided throwing in the whole scenario because it seemed really specific to my implementation, but here goes in case you’re interested:

My program in an interactive fiction engine that allows you to modify state using a planning approach known as Hierarchical Task Networks (HTNs). Currently, you define a “task” (to accomplish part of a plan) using a special predicate that has Prolog commands as part of the “condition” where the task is appropriate. Here is an example that implements “reading something that is text” (as apposed to reading something which is a book, for example):

% if Object *is* text, just read it
readObject(_, Object, Context) :- htnMethod([], Context,
    if([
        inReach(Object), 
        instanceOf(Object, idText),
        getText(Object, _, Text)
    ]),
    do([opMessage(readTextOnly, Text)]) ).

You’ll see that the Prolog commands are all part of a list which get executed by the engine manually using call/1. This is why I was asking this question, I couldn’t figure out a way to transform terms that were part of a list like that.

To tie this together with the other question I asked, what I’d really like to have is something like this:

% if Object *is* text, just read it
readObject(_, Object, Context) :- htnMethod([], Context,
    if([
        inReach(Object), 
        instanceOf(Object, idText),
        getText(Object, _, Text)
    ]),
    do([sayToUser("it says [Text]")]) ).

and have the string "it says [Text]" be transformed by the templating engine I describe in the other post.

Update: I actually got what I’d really like to have :-). The full “how” is described in the other post.

Thanks for the help!