Search code examples
prologmeta-predicate

Transform in meta-predicate at prolog


I have been reading and noticed that predicates like call are called meta-predicates and they can return other predicates as a result (Don't know if return is a good use of the word here) for example here:

assert(call(goal, X,Y)).

Edit: lurker called me to reason, this doesn't work.

I understand that it's not supposed to call predicates functions but is there a way of making assert over a predicate that will be unknown until runtime?

I want to use the same insertion predicate for more than one fact , so assert(fact(X)) does not suit my needs. I can retrieve the fact's name on runtime but how could i use assert without unifying a fact directly?


Solution

  • You should explicitly use assertz/1 or asserta/1. assert/1 is an alias for assertz/1 but only in some Prolog systems.

    The call:

    assertz(call(goal, X, Y)).
    

    will attempt to assert a fact with functor call. This is what it tries to assert in the database:

    call(goal, _, _).
    

    Since call is a functor already defined in Prolog as a predicate, it will generate an error. If you were to assert, say, the following:

    assertz(foo(goal, X, Y)).
    

    It would be successful, but what you'd get in the database is something like this:

    foo(goal, _, _).
    

    Which doesn't seem very useful. In other words, the assert is just doing what you asked it: asserting a term that you just described whose functor is call or foo in the above cases.

    If you want to assert an actual predicate, you just need to use the fact that a predicate is a term whose functor is :-. The general predicate term would be something like Head :- Body or, in canonical form, ':-'(Head, Body). This kind of term can be asserted, as long as at least Head is instantiated before the assertz call.

    assertz(':-'(Head, Body)).
    

    Or equivalently (since :- is an operator):

    assertz((Head :- Body)).
    

    If I do this:

    Head = goal, assertz((Head :- Body)).
    

    I get (using listing/0):

    :- listing.
    
    goal :-
        call(_).
    

    Not very useful. So Body really should be instantiated before making this assertz/1 call. Here then is another example:

    Head = double(X, Y), Body = (Y is X * 2), assertz((Head :- Body)).
    

    Which now results in the following:

    :- listing.
    
    double(A, B) :-
            B is A * 2.