Dynamic predicates (source preserving)

Author(s): Daniel Cabeza, The Ciao Development Team.

The package dynamic_clauses provides the assert/retract family of predicates to manipulate dynamic predicates.

The defined predicates (see dynamic_clauses_rt) allow modification of the program as it is actually running. Clauses can be added to the program (asserted) or removed from the program (retracted), as well as inspected. Note that in Ciao only the dynamic predicates of the current module (or accessible dynamic multifile predicates) can be accessed and modified. This limits the bad impact to global analysis of this dynamic modification of the program. Thus, if dynamic predicates are exported, to be able to inspect or modify them externally some accessing predicates need to be implemented and exported alongside.

For the inspecting/manipulating predicates, the argument which corresponds to the clause head must be instantiated to an atom or a compound term. The argument corresponding to the clause must be instantiated either to a term Head :- Body or, if the body part is empty, to Head. An empty body part is represented as true.

Note that using this library is very detrimental to global analysis, and that for most uses the predicates listed in Fast/concurrent update of facts suffice.

Example:

:- module(_,[test/1],[dynamic_clauses]).

% A simple (and very artificial) example of self-modifying code.

:- dynamic loop/1.

test(Xs) :-
    clause(initial_loop(Xs), Body),
    retractall(loop(_)),
    assertz((loop(Xs) :- Body)),
    loop(Xs).

:- dynamic initial_loop/1. % (otherwise we do not get the clause)
initial_loop([100|Xs]) :-
    mutate,
    loop(Xs).

mutate :-
    clause(loop([N|Xs]), Body),
    retractall(loop(_)),
    ( N = 0 ->
        assertz(loop([]))
    ; N1 is N - 1,
      assertz((loop([N1|Xs]) :- Body))
    ).

Usage and interface