.he 'DATABASE'D R A F T'Page %'
.fo 'Steven Hardy'- % -'May 1978'
.ce2
The DATABASE Package
--------------------
.sp
.ce
D R A F T  V E R S I O N
.sp
[motivation]
.sp2
This handout describes the DATABASE package.
This is a set of functions
used to manipulate a store of arbitrary items.
For example:
 	: ADD([STEVE IS WRITING A HANDOUT]);
 	: ADD([STEVE IS AT HOME]);
 	: ADD([THE PDP11 IS IN E207]);
.br
These three instructions add three 'items' to the DATABASE which is
a simple list of items, as we can see by giving the command:
 	: DATABASE =>
.br
This prints out the current DATABASE.
If there are so many items in the DATABASE that the output is difficult to
read use the 'pretty print arrow',  "==>", thus:
 	: DATABASE ==>
.br
Try these instructions to see what they do.
.bb
There is a function called PRESENT to test if
a given item is stored in the DATABASE; this function returns the item if it 
exists and FALSE otherwise, for example:
 	: PRESENT([STEVE IS AT HOME]) =>
 	** [STEVE IS AT HOME]
 	: PRESENT([STEVE IS AT WORK]) =>
 	** <FALSE>
.br
.sp
Should the state of the world change, there is a function, called
REMOVE, to delete no longer true facts from the database, thus:
 	: REMOVE([STEVE IS AT HOME]);
 	: ADD([STEVE IS AT WORK]);
.br
Try removing an item which isn`t PRESENT in the DATABASE so that
you see the type of complaint the system makes.
.bb
Suppose we wanted a function to find where STEVE is at the
moment:
 	: FUNCTION WHEREISSTEVE();
 	:	IF PRESENT([STEVE IS AT HOME])
 	:	THEN "HOME"
 	: 	ELSEIF PRESENT([STEVE IS AT WORK])
 	: 	THEN "WORK"
 	:	ELSE "UNKNOWN"
 	:	CLOSE
 	: END;
.br
.sp
Notice that by using PRESENT betwen IF and THEN we are relying on POP11
treating anything other than FALSE as TRUE. The pedant would
insist on us writing 
.br
 	: IF PRESENT(...) /= FALSE THEN
.tp5
.sp
If we now call WHEREISSTEVE, the result should be WORK, thus:
 	: WHEREISSTEVE() =>
 	** WORK
.bb
Here is a generalisation of WHEREISSTEVE:
 	: FUNCTION WHEREIS(PERSON);
 	:	IF PRESENT([^PERSON IS AT HOME])
 	:	THEN "HOME"
 	:	ELSEIF PRESENT([^PERSON IS AT WORK])
 	:	THEN "WORK"
 	:	ELSE "UNKNOWN"
 	:	CLOSE
 	: END;
.sp
The ^ indicates that the value of the following word, in
this case PERSON, is required, not the word PERSON itself.
For more details on how to use the ^ prefix type:
 	: HELP ^
.sp
Try the following:
 	: [] -> DATABASE;
 	: ADD([STEVE IS AT HOME]);
 	: ADD([AARON IS AT WORK]);
 	: WHEREIS("STEVE") =>
 	: WHEREIS("AARON") =>
 	: WHEREIS("MAX") =>
.bb
We can use the uparrow prefix to help us move people about:
 	: FUNCTION MOVE(PERSON, SOURCE, DESTINATION);
 	:	REMOVE([^PERSON IS AT ^SOURCE]);
 	:	ADD([^PERSON IS AT ^DESTINATION]);
 	: END;
 	: MOVE("STEVE", "HOME","WORK");
.br
What would you expect the following to print out?
 	: WHEREIS("STEVE") =>
.br
.sp
(If only it was that easy to get to work in real life!)
.bb
Supose I extend my area of movement:
 	MOVE("STEVE", "WORK", "REDLION");
.br
Suddenly, our WHEREIS function doesn't work:
 	: WHEREIS("STEVE") =>
 	** UNKNOWN
.br
Clearly, we could add extra tests to WHEREIS to check the pub but this
becomes unwieldy if there are many places for people to go.
What we need
is some way for WHEREIS to find my location without having to know the
possibilities in advance.
Consider the following:
.sp
 	: FUNCTION WHEREIS(PERSON);
 	:	VARS PLACE;
 	:	IF PRESENT([^PERSON IS AT ?PLACE])
 	:	THEN PLACE
 	:	ELSE "UNKNOWN"
 	:	CLOSE
 	: END;
.sp
The ? prefix instructs PRESENT to find a whole value for the
following variable
by searching through the whole list of items
in the DATABASE till it finds one matching the given 'pattern`.
(For more details of the matching process see the MATCH demo.
For the
moment your intuitions should be enough.)
.bb
We can use the ? prefix to improve MOVE, thus:
 	: FUNCTION MOVE(PERSON, DESTINATION);
 	: 	VARS SOURCE;
 	:	IF PRESENT([^PERSON IS AT ?SOURCE])THEN
 	:		REMOVE([^PERSON IS AT ^SOURCE])
 	:	CLOSE;
 	:	ADD([^PERSON IS AT ^DESTINATION])
 	: END;
.sp
This way we can move people without knowing their current location
thus:
 	: WHEREIS("MAX") =>
 	: MOVE("MAX", "WORK");
 	: WHEREIS("MAX") =>
.bb
Whilst it is convenient to use PRESENT in the MOVE function, 
it is less so in the WHEREIS function because we expect there to definitely be a stored item containing the relevant information.
There is an alternative
to PRESENT, namely LOOKUP, useful when you can assume a relevant item is
definitely
going to be a database.
LOOKUP doesn't return a result so its utility is only that as a
side effect it sets 'queried' variables
(ie words which are preceded by a ?).
If a matching item can't be
found LOOKUP reports a MISHAP.
Try the following:
 	: LOOKUP([MAX IS AT ?PLACE]);
 	: PLACE =>
 	: LOOKUP([MAGGIE IS AT ?PLACE]);
.sp
Notice that with LOOKUP and queried variables it is hardly worth writing
functions like WHEREIS, as the code becomes trivially short:
 	: FUNCTION WHEREIS(PERSON) => PLACE;
 	: 	LOOKUP([^PERSON IS AT ?PLACE])
 	: END;
.br
Try this function on a variety of arguments so you get to recognize how LOOKUP
complains when it can`t find the desired item.
.bb
What does the following function do?
 	: FUNCTION EXERCISE(P) => R;
 	:	VARS Q;
 	:	LOOKUP([?Q IS THE MOTHER OF ^P]);
 	:	LOOKUP([?R IS THE MOTHER OF ^Q]);
 	: END;
.br
Write a function to find someone`s aunt, given a DATABASE of family
relationships.
.bb
The WHEREIS function answers a question about the location of a particular person.
This question
has only one answer; if we wanted a WHOISAT function we would have
a problem since many people can be at the same place,
so the following function is inadequate, though not wholly incorrect.
 	: FUNCTION WHOISAT(PLACE) => PERSON;
 	:	LOOKUP([?PERSON IS AT ^PLACE])
 	: END;
.sp
LOOKUP and PRESENT find only one instance of their
pattern (usually the most recently ADDed) so:
 	: [] -> DATABASE;
.br
That instruction clears the DATABASE of any ADDed items.
 	: ADD([STEVE IS AT WORK]);
 	: ADD([MAX IS AT WORK]);
 	: WHOISAT("WORK") =>
 	** MAX
.sp
We need some way of finding
.ul
all
the stored items matching a pattern.
.sp
To do this we use a FOREACH loop, thus
 	: FUNCTION WHOISAT(PLACE);
 	:	VARS PERSON;
 	:	[THE FOLLOWING ARE AT ^PLACE] =>
 	:	FOREACH [?PERSON IS AT ^PLACE] THEN
 	:		PERSON =>
 	:	CLOSE;
 	:	[THATS ALL] =>
 	: END;
 	: WHOISAT("WORK");
 	: WHOISAT("HOME");
.sp
This version of WHOISAT violates one of my personal rules
of program writing - it
.ul
prints 
its results rather than
.ul
returning
them as results on the stack.
.sp
This next version of WHOISAT doesn't suffer from this defect and is
therefore much more adaptable:
 	: FUNCTION WHOISAT(PLACE);
 	: 	VARS PERSON;
 	:	[%FOREACH [?PERSON IS AT ^PLACE] THEN
 	:		PERSON
 	:	  CLOSE%]
 	: END;
.br
Notice the use of decorated list brackets, (ie. [% and %]) to collect
into a list the things left on the stack by the FOREACH loop.
Notice also the difference betwen en the two occurrences of "PERSON".
 	: WHOISAT("WORK") =>
 	: WHOISAT("HOME") =>
.br
Notice I have to
.ul
explicitly ask for the result of running WHOISAT to be printed by
following its call with a printarrow (rather than a semicolon, as with
the previous definition of WHOISTAT).
 	: MEMBER("AARON", WHOISAT("WORK")) =>
.sp
The last instruction can be translated as 'is AARON amongst
the set of people at WORK', and is virtually equivalent to a call of PRESENT.
.bb
The first exercise assumed the existence of a DATABASE describing
family relationships, for example:
 	: ADD([MARY IS THE MOTHER OF JANE]);
 	: ADD([ETHEL IS THE SISTER OF MARY]);
 	: ADD([JANE IS THE SISTER OF CLARE]);
 	: ADD([MARY IS THE WIFE OF JOHN])
.sp
Write a function, called MARRY, to ADD to the DATABASE items
representing a marriage, for example:
 	: [] -> DATABASE;
 	: MARRY("JACK", "JILL");
 	: DATABASE ==>
 	** [[JILL IS THE WIFE OF JACK]
 	    [JACK IS THE HUSBAND OF JILL]]
.sp
Here is a suitable schema for MARRY:
 	: FUNCTION MARRY(MAN, WOMAN);
 	:	ADD([^MAN IS ...]);
 	:	ADD([^WOMAN IS ...]);
 	: END;
.sp
Make your function clever enough to detect Bigamy! Also, add information
about people`s gender, eg.
 	: ADD([JACK IS A MAN]);
.br
Modify the MARRY function so that it doesn`t assume its first argument
is the MAN. That is:
 	: MARRY("ETHEL", "BERT");
.br
Should work properly. What should your program do with:
 	: MARRY("ETHEL", "JANE");
.bb
A second 'micro world` that can be represented by items in the
DATABASE is the commonly used 'domain` of children`s blocks on a table,
for example:
 	: ADD([B1 IS A BLOCK]);
 	: ADD([B2 IS A WEDGE]);
 	: ADD([B3 IS A BLOCK]);
 	: ADD([B1 SUPPORTS B2]);
 	: ADD([B3 IS LEFT OF B1]);
 	: ADD([B1 IS ON THE TABLE]);
 	: ADD([B2 IS RED]);
 	: ADD([B3 IS ON THE TABLE]);
 	: ADD([B1 IS BLUE]);
 	: ADD([B3 IS BLUE]);
.sp
This state of affairs might also be represented by a TURTLE picture:
 	9     R
 	8     RR
 	7     RRR
 	6     RRRR
 	5RRRR BBBB
 	4RRRR BBBB
 	3RRRR BBBB
 	2RRRR BBBB
 	1TTTTTTTTT
 	 123456789
.sp
What are the relative merits of these two representations? Would it
be possible to write programs to convert a description of a scene from
one form to the other?.
