In practice, we have mostly assumed that all predicates are of the
form and
for arbitrary finite sets of symbols S.
The non-membership predicates are very useful to specify in a compact
form large (potentially infinite) sets of symbols. A boolean
combination of membership and non-membership predicates can always be
written in this form, as the following table shows:
In the implementation, any boolean combination of predicates that
occurs is immediately rewritten into this atomic form. Determining
whether a symbol satisfies a predicate is trivial. Determining
satisfiability of an atomic formula is trivial too: the only atomic
formula that is not satisfiable is . The actual
computation thus involves standard operations on sets: membership,
union, intersection and difference. The implementation provides three
alternative implementations, by representing sets as ordered lists,
bit vectors or balanced binary trees.
The system also supports the addition of various application-specific predicate sets. There are various possibilities here. For instance, predicates could be expressed in terms of type hierarchies as in [4]. Another possibility is a predicate module in which predicates are membership tests of regular languages. A syntax component could be implemented by a pfsr in which predicates describe words. These predicates themselves might be implemented by finite automata over character strings. If predicates get complicated, the efficiency of checking such predicates may become important.