Setting up a database of 'facts'

The basic idea

Imagine that you have decided to collect together a database of facts, each of which is expressed as a perfectly ordinary English sentence, such as Pigs can fly. (O.K. So what is a fact? I can fly to Spain, can't I?. Indeed, I can fly to Spain more easily than I can walk to London.)

In order to take advantage of and manage such a collection of information, it is clear that at the very least you will need to be able to:

Every so often you will probably also want to: In this and the next linked page, we are going to set up the bare bones of a Logo system which is aimed at meeting these requirements through the use of four simple commands. No new Logo concepts are introduced at this stage, though various other pages (see Better interface and Eliza) offer suggestions for developing aspects of the system which require more knowledge of the language.

All of the facts we will use as examples of database entries will be single clause sentences - i.e. they will contain only one main verb. You might find it best to maintain such a restriction given the limitations of the system, but there are no consequences - whatever your decision - for the way in which the component procedures work.

Representing the facts

If each fact (or record) in the database is to have the form of - and be displayed as - an English sentence, the most obvious Logo representation for a fact is a simple list of words, like [pigs can fly]. On the further assumption that there is no compelling reason to store the various facts in subgroups - since it is arguably the business of the search mechanisms to extract sets of related facts - the database as a whole can in turn be treated as a single list of facts:

[[roses are red][violets are blue][sugar is sweet] . . .]

Creating an empty database

Preparing a new database under these conditions is quite straighforward. Suppose we call the entire database list "facts. To set up an empty database, to which new facts can then be added, we need only use make:

make "facts []

If we plan to use just one database at a time and it is always called facts, then this instruction will obviously serve both to create a fresh database and kill off an existing one. In these circumstances, it might even be worth using the instruction as the only line in the definition of a procedure, called cleanup, for example, so that the user can easily reset the database with a single-word command:

to cleanup 
make "facts [] 

Adding new facts

Although we have assumed that the order of the facts within the database as a whole is of no importance, the facts must inevitably occur in some particular order in the list called "facts. In other words, when we add a new fact to :facts it has to go somewhere not just anywhere.

Given the way in which Logo handles lists, and the primitives which are normally provided, there are two clear candidate positions for a new fact. It can be placed at the head of the current list of facts by using fput or at the end by using lput. For our present purpose the choice is arbitrary. But at this point, whichever you choose, you need to take care.

It is easy to imagine that if you want to add the fact [birds can fly] as the first fact in :facts then all you need say to Logo is:

fput [birds can fly] :facts

Reading this line as if it were pseudo English, however, is a bad idea. fput [birds can fly] :facts does not result in the modification of :facts. Worse, when you try typing it in at toplevel, you will get an error message because it is not even an instruction.

The problem is that fput is an operation which simply provides an output on the basis of the inputs it receives. In this case it would output a list composed of :facts (whatever the thing called "facts is currently) with [birds can fly] added at the beginning. The error is that you don't say what to do with this output. And on top of that, :facts is not updated because fput, like any other operation, does not modify its inputs, it merely uses them. If you want to change the value of :facts you need to say so. If, in particular, you want to have :facts take as its value the list which is output by fput [birds can fly] :facts then you must say:

make "facts fput [birds can fly] :facts

This now is an instruction - it begins with a command - and there is no error because the output of fput is used as an input make.

A command to help the user add new facts

It would, of course, be tedious to have to add every new fact to the database with an instruction as long as this. We can simplify the updating process, just as we simplified the the creation of a fresh database, by defining a more convenient command to be used at toplevel. After all, from the user's point of view, given that only one database is involved, the business of adding new information should be as simple as saying: Remember FACT. (Where FACT is some sentence.) To approach that ideal, we can define a procedure - let's call it learn, because learn is even easier to type than remember - which will take a single list, such as [emus are birds], as its input. To extend the database, the user will then need to type no more than:

learn [emus are birds]

Because of the square brackets, this is not quite English, but at least it is nearly there.

The body of learn must obviously parallel directly the form of the instruction make "facts fput [birds can fly] :facts which we used previously. The only change needed within the procedure definition is that, in order to generalise the process, the specific [birds can fly] must be replaced by a variable. Since the input to learn is always a list representing a fact, we might as well call the input variable "fact. The definition of learn, then, is:

to learn :fact 
make "facts fput :fact :facts 

Removing unwanted facts

Given that we can now say learn [. . . .], to install a fact in our database, it would be nice to be able to match that command with another called forget, which we could use to get rid of any mistakes which might accidentally creep in. Now, if we had access to some procedure remove which would accept two inputs, the first the unwanted item and the second the thing to remove it from, and would output the result of removing the first from the second, then forget could be defined following exactly the pattern of learn:
to forget :fact 
make "facts remove :fact :facts 
As it happens the Toolbox contains a definition of an operation remove which fits the bill exactly and all we need do to make forget work is to borrow it.

Notice again, however, the consequence of the fact that Logo operations do not alter the values of their inputs. It might seem natural to interpret remove :fact :facts as an instruction to create a new version of facts which no longer includes fact. This is not, however, what happens. (Compare the comments on fput, above.) Remove, like other operations, works with copies of whatever objects are passed to it, and identifies them by its own names. Remove simply outputs a list formed by removing a copy of the first object passed to it from a copy of the second object passed to it. Remove :fact :facts leaves the original fact and facts untouched. In order to update facts in forget it is thus necessary to assign to facts, as its new value, the object output by remove. Hence the form of the line: make "facts remove :fact :facts.

Top down tactics

Notice also that the strategy that we followed in arriving at a definition for forget - i.e. taking for granted the existence of remove and treating it as a black box with clear functionality even if unknown mechanisms - does not depend on the pre-existence of the procedure. If remove did not exist, it would always be possible to define it later. This tactic of temporarily presupposing the availability of lower level procedures - whose functions you are clear about and whose whose names you freely invent - so that you can focus more clearly on the essential structure of some higher level task is a generally useful one which will repay cultivation. It is one aspect of the top-down approach to programming which Logo encourages (but - see later - does not demand). Delegation of resposibility to subordinates is notable feature of the management culture of theLogo microworld. Individual Logo procedure definitions are, as a result, amost always very simple affairs.

Link to interrogation techniques.

Ron Brasington
Department of Linguistic Science
The University of Reading