HANDBOOK / GENTLE PRIMER / Describing Computations /

# Shallow Backtracking

Using shallow backtracking, a predicate invocation is elaborated as follows. The rules are tested in the given order. If a rule fails, the next rule is considered. If a rule succeeds, the predicate succeeds and the result is determined. If all rules fail, the predicate fails.

This implies that, when one considers a certain rule, one can be sure that preceding rules were not applicable.

For example, a predicate `max` computing the maximum of two integers (using the built-in predicates `ge` and `lt` for the relations greater-or-equal and less-than) that is defined by

       'rule' max(X, Y -> X): ge(X, Y)
       'rule' max(X, Y -> Y): lt(X, Y)

could also have been written as

       'rule' max(X, Y -> X): ge(X, Y)
       'rule' max(X, Y -> Y)

because the second rule is only used when `X` is less than `Y`. The disadvantage is that now the rule cannot be understood in isolation.

This style is often used for abbreviation. For example, a predicate `kind` that maps the set of letters `a`, `b`, `c`, ... , `z` onto the constants `vowl` or `consonant` can be written is this way:

       'rule'  kind(a -> vowl)
       'rule'  kind(e -> vowl)
       'rule'  kind(i -> vowl)
       'rule'  kind(o -> vowl)
       'rule'  kind(u -> vowl)

       'rule'  kind(Other -> consonant)

where the last rule replaces 21 rules dealing with consonants. Since none of the first five rules was applicable, we know that `Other` is a consonant.