[comp.lang.prolog] Meta-programming question

bradley@cs.utexas.edu (Bradley L. Richards) (08/29/90)

I'm working on a meta-programming project and have run across something I just
can't figure a way to do.  I want to locate a clause via unification with its
head, and then be able to retrieve the clause *without binding any variables*.
The goal here is to modify the clause and replace it, so it's essential that
I avoid making unintended changes via bindings.

In Arity Prolog I can achieve most of this effect by accessing the clause
using their database predicate "recorded," which returns both the clause
(which I verify via unification) and a reference number I can use for later
access.  The catch is that any comparator predicates (e.g. N \== 2) are
loused up in the returned clause.  Arity tells me that they hadn't intended
for users to access clauses with database predicates, so this isn't a
supported feature.

Can anyone suggest alternate approaches to this problem?


---------------------------------------------------------------------------
 Bradley L. Richards       uucp:  cs.utexas.edu!bradley
 bradley@cs.utexas.edu     CompuServe:  75216,1744
---------------------------------------------------------------------------

vu0310@bingvaxu.cc.binghamton.edu (R. Kym Horsell) (08/30/90)

In article <199@qt.cs.utexas.edu> bradley@cs.utexas.edu (Bradley L. Richards) writes:
>I'm working on a meta-programming project and have run across something I just
>can't figure a way to do.  I want to locate a clause via unification with its
>head, and then be able to retrieve the clause *without binding any variables*.

I hope RO'K will provide a more definitive answer, but until
the post makes it to Oz this will have to do.

A general technique (note ``logic programming'' degenerates to
a matter of ``technique'') is to use double negation to
verify whether something can be proved. For example:

verify(X) :- not(not(X)).

?- verify(X=1).

produces

X=_

The extension to unification of clause headds with goals is
straightforward.

-Kym Horsell

NEUMANN@awiwuw11.wu-wien.ac.at (Gustaf Neumann) (08/30/90)

> I'm working on a meta-programming project and have run across something
> I just can't figure a way to do.  I want to locate a clause via
> unification with its head, and then be able to retrieve the clause
> *without binding any variables*. The goal here is to modify the clause
> and replace it, so it's essential that I avoid making unintended
> changes via bindings.


I am not sure wether the following Prolog-clause is exactly what
you want, but maybe you can use it as a first approximation:

%get_clause(+,-,-)

 get_clause(Match,Head,Body) :-
        functor(Match,F,A),
        functor(Head,F,A),
        clause(Head,Body),
        \+ \+ Head=Match.


?- get_clause(member(a,[X|Y]),Head,Body).

Body = true,
Head = member(_199,[_199|_202]),
X = _65,
Y = _79 ? ;

Body = member(_199,_202),
Head = member(_199,[_201|_202]),
X = _65,
Y = _79 ?

If this version is not fast enough for your application, one should
think about a more clever representation of the clauses.

-gustaf


-------------------------------------------------------------------
Gustaf Neumann       neumann@dec4.wu-wien.ac.at, neumann@awiwuw11.bitnet
Vienna University of Economics and Business Administration
Augasse 2-6,  A-1090 Vienna, Austria
Tel: +43 (222) 347-541 x533                    Fax 347-555

ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (08/31/90)

In article <199@qt.cs.utexas.edu>, bradley@cs.utexas.edu (Bradley L. Richards) writes:
> I want to locate a clause via unification with its
> head, and then be able to retrieve the clause *without binding any variables*.
> The goal here is to modify the clause and replace it, so it's essential that
> I avoid making unintended changes via bindings.

Sigh.  This is why we wanted a Prolog standard...

It isn't perfectly clear what you don't want bound.  If you are probing
for a clause with a pattern, and you are happy for that pattern to get
instantiated, but you want (a copy of) the original clause, do

	head_clause_nobind(Head, Clause) :-
		clause(Head, _, Ref),	
		instance(Ref, Clause).

in any Edinburgh-compatible Prolog.  If you don't want the pattern to
be bound either, do

	head_nobind_clause_nobind(Head, Clause) :-
		findall(Clause, head_clause_nobind(Head,Clause), [Clause]).

/*  There is a subtle detail here.  This would work in DEC-10 Prolog, but
	head_nobind_clause_nobind(Head, Clause) :-
		findall(Ref, clause(Head, _, Ref), [Ref]),
		instance(Ref, Clause).
    would _not_ work in DEC-10 Prolog.  C Prolog, Quintus Prolog, SICStus
    Prolog, NU Prolog, and others would have no problem.
*/

> In Arity Prolog I can achieve most of this effect by accessing the clause
> using their database predicate "recorded," which returns both the clause
> (which I verify via unification) and a reference number I can use for later
> access.  The catch is that any comparator predicates (e.g. N \== 2) are
> loused up in the returned clause.  Arity tells me that they hadn't intended
> for users to access clauses with database predicates, so this isn't a
> supported feature.

I am rather surprised that 'recorded' can get at ordinary program clauses;
a large part of the reason for having 'recorded' in DEC-10 Prolog was to
make it _impossible_ to confuse database terms with clauses.

It's rather hard to suggest anything without an Arity manual.
Let's see what we might do.

	head_nobind_clause_nobind(Head, Clause) :-
		functor(Head, F, N),
		functor(Skel, F, N),
		clause(Skel, Body),
		\+ \+ Skel = Head,
		make_clause(Skel, Body, Clause).

	make_clause(Head, true, Clause) :- !, Clause = Head.
	make_clause(Head, Body, :-(Head,Body)).

This will do what I _think_ is wanted.  It is, of course, rather slow.

May I respectfully suggest that trying to use the "raw" clause store
is almost always a bad idea?  Suppose you are doing your meta-programming
and start tinkering with the append/3 predicate.  What if your meta-program
uses append/3?  You want to keep the levels separate.

I would be strongly inclined to keep the "object" clauses in their own
table.  (I'd rather keep them in a term, but on a PC the 8086's addressing
limitations are such that that's likely to be hard.)  For example, the
very simplest way of solving _this_ problem is, whenever you store an
"object" clause, instead of doing
	assert((Head :- Body))
do
	assert(object_clause(Head, Head, Body)).

What does that buy you?  Why this:

	head_clause_nobind(Goal, (Head :- Body)) :-
		object_clause(Goal, Head, Body).

In fact, for meta-programming (as John Lloyd and others have pointed out)
it may be better to use a representation in which object clauses are ground.
You could then do

	store_object_clause(Head, Body) :-
		/* Head and Body are ground terms */
		unnumbvervars(Head, Goal),
		assert(object_clause(Goal, Head, Body)).

	fetch_object_clause(Goal, Head, Body) :-
		/* Goal may be instantiated; Head, Body come back ground */
		object_clause(Goal, Head, Body).

Whether you use a ground representation or not, I strongly suggest keeping
the "object" clauses in their own table.  Amongst other benefits, that way
you can be sure that assert won't rewrite the body of one of your clauses,
because it won't realise that it _is_ a clause body.

-- 
You can lie with statistics ... but not to a statistician.

bradley@cs.utexas.edu (Bradley L. Richards) (08/31/90)

First, many thanks to all the folks who responded to my question!

>ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) writes:

>Sigh.  This is why we wanted a Prolog standard...
>
>It isn't perfectly clear what you don't want bound.  If you are probing
>for a clause with a pattern, and you are happy for that pattern to get
>instantiated, but you want (a copy of) the original clause, do
>
>        head_clause_nobind(Head, Clause) :-
>            clause(Head, _, Ref),
>            instance(Ref, Clause).

Indeed this is exactly what I want to do.  And I'm in strong agreement with
your first comment, because Arity *does not* support the clause/3 predicate.
I attempted to write one using recorded/3 when I found that it can in fact
access clauses--but, sadly, it doesn't do so in a consistent manner.

So, it looks like I'm stuck with the effective but slow version

>        head_nobind_clause_nobind(Head, Clause) :-
>            functor(Head, F, N),
>            functor(Skel, F, N),
>            clause(Skel, Body),
>            \+ \+ Skel = Head,
>            make_clause(Skel, Body, Clause).

>May I respectfully suggest that trying to use the "raw" clause store
>is almost always a bad idea?  Suppose you are doing your meta-programming
>and start tinkering with the append/3 predicate.  What if your meta-program
>uses append/3?  You want to keep the levels separate.

Indeed you are right.  I've been away from Prolog for a while so I'm not sure
if this is "standard," but Arity provides the option of creating different
"code worlds," and I keep the meta-programming stuff isolated this way.  This
has the advantage that when I want to execute a predicate in the object
program, I can merely change worlds and call the appropriate predicate.
Presuming it doesn't go off in never-never land, and that I don't want any
intermediate results, this is faster than running a metainterpreter.

Thanks again!
Bradley

---------------------------------------------------------------------------
 Bradley L. Richards       uucp:  cs.utexas.edu!bradley
 bradley@cs.utexas.edu     CompuServe:  75216,1744
---------------------------------------------------------------------------