The Dynamic Database
library(persistency) (SWI) journals dynamic predicates to disk:
17.1 Declaring and modifying
:- dynamic counter/1, visited/1.
counter(0).
increment :-
retract(counter(N)),
N1 is N + 1,
assertz(counter(N1)).
?- assertz(fact(1)). % add at the end
?- asserta(fact(0)). % add at the beginning
?- retract(fact(0)). % remove first matching clause
?- retractall(fact(_)). % remove all matching clauses (succeeds even if none)
?- abolish(fact/1). % remove predicate entirely
?- assertz((double(X, Y) :- Y is 2 * X)). % assert a rule17.2 Use cases
Caching / memoization (prefer tabling, §31).
Global state, counters, configuration.
Knowledge bases learned at runtime.
:- dynamic memo_fib/2.
mfib(N, F) :- memo_fib(N, F), !.
mfib(N, F) :-
N < 2 -> F = N
; N1 is N-1, N2 is N-2, mfib(N1, F1), mfib(N2, F2), F is F1+F2,
assertz(memo_fib(N, F)).17.3 Cautions
Assert/retract are not undone on backtracking — they break logical purity.
They make code harder to reason about and test.
In multi-threaded code, use thread_local predicates or transactions (transaction/1 in SWI 8.5+).
Alternatives: pass state as arguments, global variables (b_setval/nb_setval), tabling, library(assoc).
17.4 Persistence
library(persistency) (SWI) journals dynamic predicates to disk:
:- use_module(library(persistency)).
:- persistent user(name:atom, age:nonneg).
:- db_attach('users.db', []).
?- assert_user(ann, 30).