Databases store records that represent facts in the system. The purpose of the query interpreter is to retrieve collections of facts drawn directly from database records, as well as to deduce new facts from the database using logical inference. A fact statement in the logic language consists of one or more lists following the keyword fact. A simple fact is a single list. A dog breeder with an interest in U.S. Presidents might record the genealogy of her collection of dogs using the logic language as follows:
(fact (parent abraham barack))(fact (parent abraham clinton))(fact (parent delano herbert))(fact (parent fillmore abraham))(fact (parent fillmore delano))(fact (parent fillmore grover))(fact (parent eisenhower fillmore))
Each fact is not a procedure application, as in a Scheme expression, but instead a relation that is declared. "The dog Abraham is the parent of Barack," declares the first fact. Relation types do not need to be defined in advance. Relations are not applied, but instead matched to queries.
A query also consists of one or more lists, but begins with the keyword query. A query may contain variables, which are symbols that begin with a question mark. Variables are matched to facts by the query interpreter:
(query (parent abraham ?child))
child: barack
child: clinton
The query interpreter responds with Success! to indicate that the query matches some fact. The following lines show substitutions of the variable ?child that match the query to the facts in the database.
Compound facts. Facts may also contain variables as well as multiple sub-expressions. A multi-expression fact begins with a conclusion, followed by hypotheses. For the conclusion to be true, all of the hypotheses must be satisfied:
(fact <conclusion> <hypothesis0> <hypothesis1> ... <hypothesisN>)
For example, facts about children can be declared based on the facts about parents already in the database:
(fact (child ?c ?p) (parent ?p ?c))
The fact above can be read as: "?c is the child of ?p, provided that ?p is the parent of ?c." A query can now refer to this fact:
(query (child ?child fillmore))
child: abraham
child: delano
child: grover
The query above requires the query interpreter to combine the fact that defines child with the various parent facts about fillmore. The user of the language does not need to know how this information is combined, but only that the result has a particular form. It is up to the query interpreter to prove that (child abraham fillmore) is true, given the available facts.
A query is not required to include variables; it may simply verify a fact:
(query (child herbert delano))
A query that does not match any facts will return failure:
(query (child eisenhower ?parent))
Negation. We can check if some query does not match any fact by using the special keyword not:
(query (not <relation>))
This query succeeds if <relation> fails, and fails if <relation> succeeds. This idea is known as negation as failure.
(query (not (parent abraham clinton)))
(query (not (parent abraham barack)))
Sometimes, negation as failure may be counterintuitive to how one might expect negation to work. Think about the result of the following query:
(query (not (parent abraham ?who)))
Why does this query fail? Surely there are many symbols that could be bound to ?who for which this should hold. However, the steps for negation indicate that we first inspect the relation (parent abraham ?who). This relation succeeds, since ?who can be bound to either barack or clinton. Because this relation succeeds, the negation of this relation must fail.