Database Operations¶
Clausal supports runtime modification of the predicate database — adding and removing clauses while a program is running. This enables dynamic state, memoization, and self-modifying programs.
Quick Example¶
-dynamic(Color/1)
Color("red"),
Color("blue"),
Test("assert and query") <- (
assertz(Color("green")),
Color("green")
) # nv
The -dynamic Directive¶
By default, predicates are locked after loading — you cannot add or remove clauses at runtime. To allow runtime modification, declare the predicate as dynamic (see Directives for other directive types):
Without this directive, assertz and retract will raise a permission error.
Adding Clauses¶
assertz/1¶
assertz(Term) — add a fact at the end of the clause list (like Prolog's
assertz).
asserta/1¶
asserta(Term) — add a fact at the beginning of the clause list (like
Prolog's asserta). The new clause will be tried first on subsequent queries.
-dynamic(Priority/1)
Priority("low"),
Test("assert first") <- (
asserta(Priority("high")),
Priority("high")
) # nv
Removing Clauses¶
retract/1¶
retract(Term) — remove the first clause whose head unifies with Term.
-dynamic(Item/1)
Item("a"),
Item("b"),
Item("c"),
Test("retract") <- (
retract(Item("b")),
(not Item("b")),
Item("a"),
Item("c")
) # nv
retract uses unification for matching, so you can retract by pattern:
-dynamic(Pair/2)
Pair("x", 1),
Pair("y", 2),
Pair("z", 3),
Test("retract by pattern") <- (
retract(Pair("y", _)),
(not Pair("y", 2))
) # nv
Table Management¶
abolish_table/2¶
abolish_table(functor, Arity) — clear cached answers for a specific tabled
predicate.
abolish_all_tables/0¶
abolish_all_tables() — clear all tabling caches at once.
Patterns & Recipes¶
Memoization¶
Use assert to cache computed results:
-dynamic(FibCache/2)
Fib(0, 0),
Fib(1, 1),
Fib(N, F) <- (
N > 1,
N1 is N - 1,
N2 is N - 2,
Fib(N1, F1),
Fib(N2, F2),
F is F1 + F2,
assertz(FibCache(N, F))
)
(For automatic memoization, consider -table instead.)
Counter / mutable state¶
-dynamic(Ctr/1)
Ctr(0),
Increment(NEW) <- (
retract(Ctr(OLD)),
NEW is OLD + 1,
assertz(Ctr(NEW))
)
Test("counter") <- (
Increment(1),
Increment(2),
Ctr(2)
)
Collecting facts from a computation¶
-dynamic(Result/1)
CollectEvens(LIST) <- (
in_(X, LIST),
X % 2 == 0,
assertz(Result(X)),
False
)
CollectEvens(_),
(Prefer findall for this pattern — it is cleaner and
does not require dynamic predicates.)
Gotchas¶
- Must declare
-dynamic— without it, assertz/retract raise a permission error. This is intentional: it prevents accidental modification of predicates that should be stable. - assertz adds facts, not rules —
assertz(foo(X) <- bar(X))is not supported. Only ground or partially-ground facts can be asserted. - retract removes one clause — it removes the first matching clause only.
Call it in a loop (or use
findall+ multiple retracts) to remove all matches. - Order matters —
assertzappends,assertaprepends. The clause order affects which solution is found first. - Tabling interaction — if a tabled predicate depends on dynamic facts,
remember to
abolish_tableafter modifying the facts, or the cached answers will be stale.
See also: Directives — -dynamic and other predicate
directives, Tabling — automatic memoization with -table,
Meta-Predicates — findall as an alternative to
assert-based collection.