2Predicates

More interesting goals are created by applying a special kind of Racklog object called a predicate (or relation) to other Racklog objects. Racklog comes with some primitive predicates, such as the arithmetic operators %=:= and %<, standing for arithmetic “equal” and “less than” respectively. For example, the following are some goals involving these predicates:

 > (%which () (%=:= 1 1)) '() > (%which () (%< 1 2)) '() > (%which () (%=:= 1 2)) #f > (%which () (%< 1 1)) #f

Other arithmetic predicates are %> (“greater than”), %<= (“less than or equal”), %>= (“greater than or equal”), and %=/= (“not equal”).

Racklog predicates are not to be confused with conventional Racket predicates (such as < and =). Racklog predicates, when applied to arguments, produce goals that may either succeed or fail. Racket predicates, when applied to arguments, yield a boolean value. Henceforth, we will use the term “predicate” to mean Racklog predicates. Conventional predicates will be explicitly called “Racket predicates”.

2.1Predicates Introducing Facts

Users can create their own predicates using the Racklog form %rel. For example, let’s define the predicate %knows:

 (define %knows (%rel () [('Odysseus 'TeX)] [('Odysseus 'Racket)] [('Odysseus 'Prolog)] [('Odysseus 'Penelope)] [('Penelope 'TeX)] [('Penelope 'Prolog)] [('Penelope 'Odysseus)] [('Telemachus 'TeX)] [('Telemachus 'calculus)]))

The expression has the expected meaning. Each clause in the %rel establishes a fact: Odysseus knows TeX, Telemachus knows calculus, &c. In general, if we apply the predicate to the arguments in any one of its clauses, we will get a successful goal. Thus, since %knows has a clause that reads [('Odysseus 'TeX)], the goal (%knows 'Odysseus 'TeX) will be true.

We can now get answers for the following types of queries:

 > (%which () (%knows 'Odysseus 'TeX))

'()

 > (%which () (%knows 'Telemachus 'Racket))

#f

2.2Predicates with Rules

Predicates can be more complicated than the above bald recitation of facts. The predicate clauses can be rules, eg,

 (define %computer-literate (%rel (person) [(person) (%knows person 'TeX) (%knows person 'Racket)] [(person) (%knows person 'TeX) (%knows person 'Prolog)]))

This defines the predicate %computer-literate in terms of the predicate %knows. In effect, a person is defined as computer-literate if they know TeX and Racket, or TeX and Prolog.

Note that this use of %rel employs a local logic variable called person. In general, a %rel-expression can have a list of symbols as its second subform. These name new logic variables that can be used within the body of the %rel.

The following query can now be answered:

 > (%which () (%computer-literate 'Penelope))

'()

Since Penelope knows TeX and Prolog, she is computer-literate.

2.3Solving Goals

The above queries are yes/no questions. Racklog programming allows more: We can formulate a goal with uninstantiated logic variables and then ask the querying process to provide, if possible, values for these variables that cause the goal to succeed. For instance, the query:

 > (%which (what) (%knows 'Odysseus what))

'((what . TeX))

asks for an instantiation of the logic variable what that satisfies the goal (%knows 'Odysseus what). In other words, we are asking, “What does Odysseus know?”

Note that this use of %which like %rel in the definition of %computer-literate uses a local logic variable, what. In general, the second subform of %which can be a list of local logic variables. The %which-query returns an answer that is a list of bindings, one for each logic variable mentioned in its second subform. Thus,

 > (%which (what) (%knows 'Odysseus what))

'((what . TeX))

But that is not all that wily Odysseus knows. Racklog provides a zero-argument procedure (“thunk”) called %more that retries the goal in the last %which-query for a different solution.

 > (%more) '((what . Racket))

We can keep pumping for more solutions:

 > (%more) '((what . Prolog)) > (%more) '((what . Penelope)) > (%more) #f

The final #f shows that there are no more solutions. This is because there are no more clauses in the %knows predicate that list Odysseus as knowing anything else.

2.4Asserting Extra Clauses

We can add more clauses to a predicate after it has already been defined with a %rel. Racklog provides the %assert! form for this purpose. Eg,

 (%assert! %knows () [('Odysseus 'archery)])

tacks on a new clause at the end of the existing clauses of the %knows predicate. Now, the query:

 > (%which (what) (%knows 'Odysseus what))

'((what . TeX))

gives TeX, Racket, Prolog, and Penelope, as before, but a subsequent (%more) yields a new result:

 > (%more) '((what . archery))

The Racklog form %assert-after! is similar to %assert! but adds clauses before any of the current clauses.

Both %assert! and %assert-after! assume that the variable they are adding to already names a predicate (presumably defined using %rel). In order to allow defining a predicate entirely through %assert!s, Racklog provides an empty predicate value %empty-rel. %empty-rel takes any number of arguments and always fails. A typical use of the %empty-rel and %assert! combination:

 (define %parent %empty-rel) (%assert! %parent () [('Laertes 'Odysseus)]) (%assert! %parent () [('Odysseus 'Telemachus)] [('Penelope 'Telemachus)])

(Racklog does not provide a predicate for retracting assertions, since we can keep track of older versions of predicates using conventional Racket features (let and set!).)

2.5Local Variables

The local logic variables of %rel- and %which-expressions are in reality introduced by the Racklog syntactic form called %let. (%rel and %which are macros written using %let.)

%let introduces new lexically scoped logic variables. Supposing, instead of

 > (%which (what) (%knows 'Odysseus what))

'((what . TeX))

 > (%let (what) (%which () (%knows 'Odysseus what)))

'()

This query, too, succeeds five times, since Odysseus knows five things. However, %which emits bindings only for the local variables that it introduces. Thus, this query emits () five times before (%more) finally returns #f.

However, note that, like conventional Racket let, the body forms of %let are evaluated in turn with the last form in tail position. Thus, to combine multiple goals involving a new logical variable introduced by %let, it is necessary to wrap the body in an %and form.

For example:

 > (%which (d) (%let (a p) (%and (%= p (cons 1 2)) (%= p (cons a d)))))

'((d . 2))

whereas

 > (%which (d) (%let (a p) (%= p (cons 1 2)) ; This goal is ignored (%= p (cons a d))))

'((d . _))