XSB provides an array of features for modifying the dynamic database. As a default, using assert/1, clauses can be asserted using first-argument indexing in a manner that is now standard to Prolog implementations. However, a variety of other behaviors can be specified using the (executable) directives index/3 and index/2. For instance, dynamic clauses can be declared to have multiple or joint indexes, and this indexing can be either hash-based as is typical in Prolog systems or based on tries. No matter what kind of indexing is used, space is dynamically allocated when a new clause is asserted and, unless specified otherwise, released after it is retracted. Furthermore, the size of any index table expands dynamically as clauses are asserted.
All dynamic predicates are compiled into SLG-WAM code, however the
manner of their compilation may differ, and the differences in
compilation affect the semantics for the predicate. If a dynamic
predicate
is given an indexing directive of trie, clauses
for
will be compiled using trie instructions; otherwise clauses
for
will be compiled into SLG-WAM instructions along the lines
of static predicates.
Consider first dynamic predicates that use any indexing other than trie - including multiple or joint indices and star indexing. XSB asserts WAM code for such clauses so that that the execution time of dynamic code is similar to compiled code for unit and binary clauses. Furthermore, tabling can be used by explicitly declaring a predicate to be both dynamic and tabled. In Version 3.0, when the clause of a dynamic predicate is asserted as WAM code, the ``immediate semantics'' rather than the ISO Semantics of assert/retract [40]. The immediate semantics allows assert and retract to be fast and spatially efficient, but requires that significant care must be taken when modifying the definition of a predicate which is currently being executed.
If a dynamic predicate is given an indexing directive of trie, clauses of the predicate are compiled (upon a call assert/1) using trie instructions as described in [46]. Creation of trie-based dynamic code is significantly faster than creation of other dynamic code, and execution time may also be faster. However, trie-based predicates can only be used for unit clauses where a relation is viewed as a set, and where the order of the facts is not important.
XSB does not at this time fully support dynamic predicates defined
within compiled code. The only way to generate dynamic code is by
explicitly asserting it, or by using the standard predicate load_dyn/1 to read clauses from a file and assert them (see the
section Asserting Dynamic Code in Volume 2). There is a dynamic/1 predicate (see page
) that declares a
predicate within the system so that if the predicate is called when no
clauses are presently defining it, the call will quietly fail instead
of issuing an ``Undefined predicate'' error message.
Note that because of the precedence of :-/2, asserting a clause containing this operator requires an extra set of parentheses: assert((Head :- Body)).
Error Cases
Note that because of the precedence of :-/2, asserting a clause containing this operator requires an extra set of parentheses: assert((Head :- Body)).
Note that because of the precedence of :-/2, asserting a clause containing this operator requires an extra set of parentheses: assert((Head :- Body)).
The default implementation of non-trie-indexed assert generates code with a single pass through the asserted term. Because of this, it cannot know when it has encountered the final occurrence of a variable, and thus it can never release (and thus re-use) registers that are used to refer to variables. Since there is a limit of 255 registers in the XSB virtual machine, asserting a clause with more than this many distinct variables results in an error. There is an alternative implementation of assert that initially traverses the clause to determine the number of occurrences of each variable and thus allows better use of registers during code generation.
Clause is the clause to assert. AorZandVar is an integer whose lower 2 bits are used: The low-order bit is 0 if the clause is to be added as the first clause, and 1 if it is to be added as the last clause. If the second bit (2) is on, then the clause is traversed to count variable occurrences and so improve register allocation for variables; if it is 0, the default one-pass code-generation is done. So, for example, if AorZandVar is 3, then the clause will be asserted as the last one in the predicate and the better register allocation will be used. Index indicates the argument(s) on which to index.
The technical details on space reclamation are as follows. When retract is called, a check is made to determine whether it is safe to reclaim space for that clause. Safety is ensured when:
Error Cases
It is an error to abolish a predicate when there is more than 1 active thread, regardless of whether the predicate is thread-private or thread-shared. The reason for this is that, even if PredInd denotes a thread-private predicate, one thread may be making use of PredInd as another thread abolishes it. abolish/1 throws an error in such a case to prevent such a semantic inconsistency. Similarly, if there is a non-completed table for PredInd, an error is thrown to prevent incompleteness in the tabled computation.
ISO Compatability Note: Version 3.0 of XSB allows static predicates to be abolished and their space reclaimed. Such space is reclaimed immediately, and unlike the case for abolished static code, no check is made to ensure that XSB's choice point stack is free of choice points for the abolished static predicate. Abolishing static code is thus dangerous and should be avoided unless a user is certain it is safe to use.
Error Cases
Error Cases
By default, gc_dynamic/1 is called automatically at the top level of the XSB interpreter, when abolishing a predicate, and when calling retractall for an ``open'' term containing no variable bindings.
In general, XSB supports hash-based indexing on various arguments or combinations of arguments, along with trie-based indexing. The availability of various kinds of indexing depends on whether code is static (e.g. compiled) or dynamic (e.g. asserted or loaded with load_dyn/1). The executable directive index/2 does not re-index an already existing predicate but takes effect only for clauses asserted after the directive has been given. Index directives can be given to the compiler as part of source code or executed during program execution (analogously to op/3).
An index is usually on a single argument, in which case the IndexElt consists of a single argument indicator. However, sometimes one wants an index on multiple arguments, meaning that the values of several arguments are to be concatenated to create the search key of the index. Such a multi-argument (or joint) index is indicated by using an IndexElt that has up to 3 argument indicators separated by the + (plus) operator, e.g., 1+2+3.
For example, index(p/3,[2,1]) indicates that clauses asserted to the predicate p/3 should be indexed on both the second and the first argument. Subsequent calls to p/3 will first check to see if the second argument is non-variable, and if so use that index, using the main functor symbol of that argument. If the second argument is variable, it will next check to see if the first argument is non-variable and if so, use that index, built on the main functor symbol of the first argument.
index(p/3,[*(2),1]) would result in similar behavior as the previous example, but the first index to be tried (on the second argument) would be built using more of the term value in that second argument position (not just the main functor symbol.)
As another example, one could specify: index(p/5,[1+2,1,4]). After clauses are asserted to it, a call to p/5 would first check to see if both the first and second arguments are non-variable and if so, use an index based on both those values. Otherwise, it would see if the first argument is non-variable and if so, use an index based on it. Otherwise, it would see if the fourth argument is non-variable and if so use an index based on it. As a last resort, it would use no index but backtrack through all the clauses in the predicate. In all these cases, the indexes are built using only the main functor symbol in the indicated argument position. (Notice that it may well make sense to include an argument that appears in a joint specification later alone, as 1 in this example, but it never makes sense forcing the single argument to appear earlier. In that case the joint index would never be used.)
If we want to use similar indexing on p/5 of the previous example, except say argument 1 takes on complex term values and we want to index on more of those terms, we might specify the index as index(p/5,[*(1)+2,*(1),4]).
Despite these advantages, representing terms as tries leads to semantic differences from asserted code, of which the user should be aware. First, the order of clauses within a trie is arbitrary: using asserta/1 or assertz for a predicate currently using trie indexing will give the same behavior as using assert. Also, the current version of XSB only allows trie indexing for unit clauses.
Trie-based indexing is available only for dynamic predicates.