Generating rule with variables


I’d like to generate a rule with variables, based on ground facts.

Suppose I have:

R = rule(x,z) :- fact(x, y), fact(y,z), transform(R, R_out).

R_out = rule(X-x, Z-z) :- fact(X-x, Y-y), fact(Y-y, Z-z).

The idea is that R_out becomes a callable rule.

Essentially, generating corresponding variables, but retaining the source grounds for each via pair.

Its a kind of complication steps, to turn data into a rule.

I guess it required the ability to generate variables, store and retrieve them as new term is constructed.

Can something like this be done.



Looks like i am starting to stitch (hack?) together some ideas.

First, its possible to generate fresh and unique variables with an underscore,

Its also possible to store them in a list, such as


represents a list of three fresh, unique variables.

Its also possible to attach a key to each, such as so:

[a-_, b-_], 

provides a list of two pairs, each pair having a fresh variable that can be retrieved via member.

member(a-X, List_of_Pairs], would bind X to the unique variable X.

And, then there is term construction such as =…

Now i need to stitch this together:


some more thinking – creating a key list of new vars

new_var(Key, L0, L) :-
   append(L0, [Key-_], L).

This can be done quite easily and perhaps easier than you imagine. Your opening query was a bit confusing though, I think you would do better to include parentheses and precise syntax.

Did you mean:

R = (rule(x,z) :- fact(x, y), fact(y,z)),
transform(R, R_out),
R_out = (rule(X-x, Z-z) :- fact(X-x, Y-y), fact(Y-y, Z-z)).

but maybe what you really wanted was
R_out = (rule(X, Z) :- fact(X, Y), fact(Y, Z)).
and Map=[X=x, Y=y, Z=z].

Generating the map is as easy as
zip(_Vars, Atoms, Map),

R_out could be produced in various ways. One way would be

transform(R, R_out) :-
leaves(R, Leaves),
zip(_Vars, Leaves, Map),
replace_leaves(R, Map, R_out).

leaves is just a recursive predicate on terms that yields the atomics.

replace_leaves is a similar predicate on terms that takes apart terms, recurses on the arguments, replaces leaf terms using the map, and sews compounds back up with the replacements.

thank you for your comments – it is indeed R_out i am looking for.

The idea is to regenerate the facts as a rule while ensuring that variables are correctly shared across clauses.

I could then call R_out and get the clauses process the fact via regular prolog processing

Then is the above sufficient for you to proceed?



I guess I wasnt complete in my problem description.

I want to be able to do this transformation for any number of clauses with any number of shared arguments.

So, this requires some kind of loop with an accumulator that generates the R_Out, while ensuring that generated variables are correctly inserted where needed.


ok, this is what you meant as well.

But, its unclear to me how you address the generation of variables, and ensuring that corresponding variables are inserted in the right location in the recreated facts.

The atom-var pairs are a helper construction I envisioned, to enable the repeat lookup of the same variable to be inserted into a different fact.

So, if i need to insert a variable for x, i would look up x-X, and insert x-X, thereby ensuring that a shared variable X is inserted across two facts, where x was located in R.

Since some time, there is foldsubterms/5 for many recursive term inspection or modification:

:- use_module(library(terms)).

revar(Term, VarTerm, Bindings) :-
    foldsubterms(revar, Term, VarTerm, [], Bindings).

revar(Atom, Var, Bindings0, Bindings) :-
    (   memberchk(Atom=Var, Bindings0)
    ->  Bindings = Bindings0
    ;   Bindings = [Atom=Var|Bindings0]

var_name(Name) :-


And now

?- R = (rule(x,z) :- fact(x, y), fact(y,z)), revar(R, R_out, Map).
R = (rule(x, z):-fact(x, y), fact(y, z)),
R_out = (rule(_A, _B):-fact(_A, _C), fact(_C, _B)),
Map = [y=_C, z=_B, x=_A].
1 Like

Wow – this needs studying :slight_smile:

I never knew this existed. My answer manually constructed equivalent but in two passes:

  1. collect atoms
  2. convert atom list to a map
  3. replace atoms using map (which would have involved a lot of calls to functor and arg).