.he 'CONVERSE''Page %'
.fo 'Steven Hardy'- % -'April 1978'
.ce 2
ASSOC and other functions
=========================

This handout contains the ASSOC function we discussed in the lectures
and the extensions to REPLYTO to let us use it.
 	: FUNCTION ASSOC(LIST, KEY);
 	:      IF LIST = []
 	:      THEN UNDEF
 	:      ELSEIF KEY = HD(HD(LIST))
 	:      THEN HD(TL(HD(LIST)))
 	:      ELSE ASSOC(TL(LIST), KEY)
 	:      CLOSE
 	: END;
 	: 
 	: VARS STEVE;
 	: [[NAME STEVE] [AGE 28] [HAIR BROWN] [LEGS TWO]]
 	:      -> STEVE;
.br
The ASSOCiation list held in the variable STEVE is meant to describe
me. It is a list of two element lists; these two element lists each
describe one property of me - in the format [key value].
 	: ASSOC(STEVE, "AGE")=>
 	** 28
.br
I put AGE in word quotes (") because I want the key to be the word AGE
itself - not the value of the variable AGE. I didn't put STEVE in word
quotes because I do want the value of the variable.
 	: TRACE ASSOC;
 	: ASSOC(STEVE, "AGE")=>
 	>ASSOC [[NAME STEVE] [AGE 28] [HAIR BROWN] [LEGS TWO]] AGE
 	!>ASSOC [[AGE 28] [HAIR BROWN] [LEGS TWO]] AGE
 	!<ASSOC 28
 	<ASSOC 28
 	** 28
 	: ASSOC(STEVE, "CAPITAL")=>
 	>ASSOC [[NAME STEVE] [AGE 28] [HAIR BROWN] [LEGS TWO]] CAPITAL
 	!>ASSOC [[AGE 28] [HAIR BROWN] [LEGS TWO]] CAPITAL
 	!!>ASSOC [[HAIR BROWN] [LEGS TWO]] CAPITAL
 	!!!>ASSOC [[LEGS TWO]] CAPITAL
 	!!!!>ASSOC [] CAPITAL
 	!!!!!<ASSOC UNDEF
 	!!!!<ASSOC UNDEF
 	!!!<ASSOC UNDEF
 	!!<ASSOC UNDEF
 	!<ASSOC UNDEF
 	<ASSOC UNDEF
 	** UNDEF
.br
I don't have a capital city!
 	: UNTRACE ASSOC;
.br
I now want to write a program to interogate a data base represented by
ASSOCiation lists. I would like to be able to ask questions of the
form:
[WHAT IS THE AGE OF STEVE]
.ti -5
or perhaps:
[WHAT IS THE CAPITAL OF FRANCE]
If the question asked is of the form:
 	: [WHAT IS THE = OF =]
.br
(where "=" indicates that any thing go in at that point) my program must
look up (with ASSOC) the value associated with the first = on the
ASSOCiation list representing the second =. The first = is the fourth
element of the input list and the second = is the sixth. I think I
will need two functions; one to say if the input ISLIKE the above
pattern and one to get the n'th element of a list:
 	: OPERATION ISLIKE LIST TEMPLATE;
 	:      IF LIST = []
 	:      THEN TEMPLATE = []
 	:      ELSEIF TEMPLATE = []
 	:      THEN FALSE
 	:      ELSEIF HD(LIST) = HD(TEMPLATE) OR HD(TEMPLATE) = "="
 	:      THEN TL(LIST) ISLIKE TL(TEMPLATE)
 	:      ELSE FALSE
 	:      CLOSE
 	: END;
.br
One of the problems with POP11 is that a lot of the time it looks a bit like a weird
version of English; this gives us various expectations which are not fulfilled by this function.
In English, the sentence 'If list is nil then template is nil` is an assertion;
in POP11 the (partial) expression 'IF LIST = [] THEN TEMPLATE = []`
is an instruction which should be read as 'if the result of applying the function "=" to
LIST and [] returns TRUE then apply the function "=" to TEMPLATE and [] - the result
of this second function call (be it TRUE or FALSE)
is the result of the IF expression`.

One way of writing ISLIKE that is easier to understand is:
 	: OPERATION ISLIKE LIST TEMPLATE;
 	:      IF LIST = []
 	:      THEN IF TEMPLATE = [] THEN TRUE ELSE FALSE CLOSE
 	:      ...
.br
However, any expression of the form:
 	: IF <EXPR> THEN TRUE ELSE FALSE CLOSE
.br
is TRUE if the <expr> is TRUE and FALSE if the <expr> is FALSE, so
one
might just as well write <expr> alone.

Since ISLIKE is defined as an OPERATION, it can be written between
its arguments.  E.g. LISTA ISLIKE LISTB.
.br
Lets see this function in action.
 	: TRACE ISLIKE;
 	: [WHAT IS THE AGE OF STEVE] ISLIKE [WHAT IS THE = OF =] =>
 	>ISLIKE [WHAT IS THE AGE OF STEVE] [WHAT IS THE = OF =]
 	!>ISLIKE [IS THE AGE OF STEVE] [IS THE = OF =]
 	!!>ISLIKE [THE AGE OF STEVE] [THE = OF =]
 	!!!>ISLIKE [AGE OF STEVE] [= OF =]
 	!!!!>ISLIKE [OF STEVE] [OF =]
 	!!!!!>ISLIKE [STEVE] [=]
 	!!!!!!>ISLIKE [] []
 	!!!!!!<ISLIKE <TRUE>
 	!!!!!<ISLIKE <TRUE>
 	!!!!<ISLIKE <TRUE>
 	!!!<ISLIKE <TRUE>
 	!!<ISLIKE <TRUE>
 	!<ISLIKE <TRUE>
 	<ISLIKE <TRUE>
 	** <TRUE>
 	: [WHAT IS THE WEATHER LIKE] ISLIKE [WHAT IS THE = OF =]=>
 	>ISLIKE [WHAT IS THE WEATHER LIKE] [WHAT IS THE = OF =]
 	!>ISLIKE [IS THE WEATHER LIKE] [IS THE = OF =]
 	!!>ISLIKE [THE WEATHER LIKE] [THE = OF =]
 	!!!>ISLIKE [WEATHER LIKE] [= OF =]
 	!!!!>ISLIKE [LIKE] [OF =]
 	!!!!<ISLIKE <FALSE>
 	!!!<ISLIKE <FALSE>
 	!!<ISLIKE <FALSE>
 	!<ISLIKE <FALSE>
 	<ISLIKE <FALSE>
 	** <FALSE>
 	: UNTRACE ISLIKE;
.br
Now I am in a position to write a version of REPLYTO:
 	: FUNCTION REPLYTO(INPUT);
 	:   IF INPUT ISLIKE [WHAT IS THE = OF =] THEN
 	:     ASSOC(VALOF(INPUT(6)), INPUT(4))
 	:   ELSE
 	:     [I DO NOT UNDERSTAND YOU]
 	:   CLOSE
 	: END;
.br
The denotation INPUT(6) means 'the sixth element of the list INPUT'.
 	: FUNCTION CONVERSE();
 	:  VARS INPUT;
 	:  LISTREAD() -> INPUT;
 	:  IF INPUT = [BYE] THEN
 	:    [BYE THEN] =>
 	:  ELSE
 	:    REPLYTO(INPUT) =>
 	:    CONVERSE();
 	:  CLOSE
 	: END;
 	: CONVERSE();
 	: [WHAT IS THE AGE OF STEVE]
 	** 28
 	: [WHAT IS THE CAPITAL OF STEVE]
 	** UNDEF
 	: [HOW NOW BROWN COW]
 	** [I DO NOT UNDERSTAND YOU]
 	: [BYE]
 	** [BYE THEN]
 	: TRACE ISLIKE ASSOC REPLYTO;
 	: CONVERSE();
 	: [WHAT IS THE HAIR OF STEVE]
 	>REPLYTO [WHAT IS THE HAIR OF STEVE]
 	!>ISLIKE [WHAT IS THE HAIR OF STEVE] [WHAT IS THE = OF =]
 	!!>ISLIKE [IS THE HAIR OF STEVE] [IS THE = OF =]
 	!!!>ISLIKE [THE HAIR OF STEVE] [THE = OF =]
 	!!!!>ISLIKE [HAIR OF STEVE] [= OF =]
 	!!!!!>ISLIKE [OF STEVE] [OF =]
 	!!!!!!>ISLIKE [STEVE] [=]
 	!!!!!!!>ISLIKE [] []
 	!!!!!!!<ISLIKE <TRUE>
 	!!!!!!<ISLIKE <TRUE>
 	!!!!!<ISLIKE <TRUE>
 	!!!!<ISLIKE <TRUE>
 	!!!<ISLIKE <TRUE>
 	!!<ISLIKE <TRUE>
 	!<ISLIKE <TRUE>
 	!>ASSOC [[NAME STEVE] [AGE 28] [HAIR BROWN] [LEGS TWO]] HAIR
 	!!>ASSOC [[AGE 28] [HAIR BROWN] [LEGS TWO]] HAIR
 	!!!>ASSOC [[HAIR BROWN] [LEGS TWO]] HAIR
 	!!!<ASSOC BROWN
 	!!<ASSOC BROWN
 	!<ASSOC BROWN
 	<REPLYTO BROWN
 	** BROWN
 	: [WHAT IS THE WEATHER LIKE TODAY]
 	>REPLYTO [WHAT IS THE WEATHER LIKE TODAY]
 	!>ISLIKE [WHAT IS THE WEATHER LIKE TODAY] [WHAT IS THE = OF =]
 	!!>ISLIKE [IS THE WEATHER LIKE TODAY] [IS THE = OF =]
 	!!!>ISLIKE [THE WEATHER LIKE TODAY] [THE = OF =]
 	!!!!>ISLIKE [WEATHER LIKE TODAY] [= OF =]
 	!!!!!>ISLIKE [LIKE TODAY] [OF =]
 	!!!!!<ISLIKE <FALSE>
 	!!!!<ISLIKE <FALSE>
 	!!!<ISLIKE <FALSE>
 	!!<ISLIKE <FALSE>
 	!<ISLIKE <FALSE>
 	<REPLYTO [I DO NOT UNDERSTAND YOU]
 	** [I DO NOT UNDERSTAND YOU]
 	: [BYE]
 	** [BYE THEN]
 	: UNTRACE ISLIKE ASSOC REPLYTO;
.br
There is a problem with this program; if I refer to an object it
hasn't heard of it goes wrong. This is because the VALOF(LIST(6))
will be an ugly object with UNDEF as its tail and ASSOC will cause an
error if given such an object as its list. To get round this problem I
will write a simple function called GETVAL to take care of the
problem:
 	: FUNCTION GETVAL(WORD);
 	:   IF IDENTPROPS(WORD) = UNDEF THEN
 	:     [[NAME %WORD%]] -> VALOF(WORD)
 	:  CLOSE;
 	:  VALOF(WORD)
 	: END;
.br
I will leave you to determine, by experiment if necessary, how this
function works. And now an extension of REPLYTO:
 	: FUNCTION REPLYTO(INPUT);
 	:   IF INPUT ISLIKE [WHAT IS THE = OF =] THEN
 	:     ASSOC(GETVAL(INPUT(6)), INPUT(4))
 	:   ELSEIF INPUT ISLIKE [THE = OF = IS =] THEN
 	:     [%INPUT(2), INPUT(6)%] :: GETVAL(INPUT(4))
 	:        -> VALOF(INPUT(4));
 	:     [OK - I HAVE NOTED THAT FACT]
 	:   ELSE [I DO NOT UNDERSTAND YOU]
 	:   CLOSE
 	: END;
 	: CONVERSE();
 	: [WHAT IS THE AGE OF STEVE]
 	** 28
 	: [WHAT IS THE CAPITAL OF FRANCE]
 	** UNDEF
 	: [THE CAPITAL OF FRANCE IS PARIS]
 	** [OK - I HAVE NOTED THAT FACT]
 	: [THE MAININDUSTRY OF KENT IS MARKETGARDENING]
 	** [OK - I HAVE NOTED THAT FACT]
 	: [THE KING OF FRANCE IS DEAD]
 	** [OK - I HAVE NOTED THAT FACT]
 	: [WHAT IS THE KING OF FRANCE]
 	** DEAD
 	: [WHAT IS THE CAPITAL OF FRANCE]
 	** PARIS
 	: [BYE]
 	** [BYE THEN]
.br
Now I want to extend the function so that it can deal with questions
of the form [IS THE = OF = =], for example [IS THE CAPITAL OF FRANCE
PARIS].
 	: FUNCTION REPLYTO(INPUT);
 	:   IF INPUT ISLIKE [WHAT IS THE = OF =] THEN
 	:     ASSOC(GETVAL(INPUT(6)), INPUT(4))
 	:   ELSEIF INPUT ISLIKE [THE = OF = IS =] THEN
 	:     [%INPUT(2)%] :: GETVAL(INPUT(4))
 	:        -> VALOF(INPUT(4));
 	:     [OK - I HAVE NOTED THAT FACT]
 	:   ELSEIF INPUT ISLIKE [IS THE = OF = =] THEN
 	:     IF REPLYTO([WHAT IS THE %INPUT(3)% OF %INPUT(5)%])
 	:       = (INPUT(6)) THEN "YES" ELSE "NO" CLOSE
 	:   ELSE [I DO NOT UNDERSTAND YOU]
 	:   CLOSE
 	: END;
 	: TRACE REPLYTO;
 	: CONVERSE();
 	: [IS THE AGE OF STEVE 28]
 	>REPLYTO [IS THE AGE OF STEVE 28]
 	!>REPLYTO [WHAT IS THE AGE OF STEVE]
 	!<REPLYTO 28
 	<REPLYTO YES
 	** YES
 	: [BYE]
 	** [BYE THEN]
 	: UNTRACE REPLYTO;
.br
Notice how convenient it is to have REPLYTO leave its result on the
stack rather than print it directly - for now we can use REPLYTO to
answer more complicated questions.

By the time a function gets to be bigger than about ten lines I start
wanting to split it up into several simpler functions. I will do this
by writing a function to handle each class of question, for example:
 	: FUNCTION WHATIS(KEY, OBJECT);
 	:   ASSOC(GETVAL(OBJECT), KEY)
 	: END;
 	: FUNCTION THEIS(KEY, OBJECT, VALUE);
 	:   [%KEY, VALUE%] :: GETVAL(OBJECT) -> VALOF(OBJECT);
 	:   [OK - I HAVE NOTED THAT FACT]
 	: END;
 	: FUNCTION ISTHE(KEY, OBJECT, VALUE);
 	:   IF REPLYTO([WHAT IS THE %KEY% OF %OBJECT%]) = VALUE THEN
 	:     "YES"
 	:   ELSE
 	:     "NO"
 	:   CLOSE
 	: END;
 	: FUNCTION REPLYTO(INPUT);
 	:   IF INPUT ISLIKE [WHAT IS THE = OF =] THEN
 	:     WHATIS(INPUT(4), INPUT(6))
 	:   ELSEIF INPUT ISLIKE [THE = OF = IS =] THEN
 	:     THEIS(INPUT(2), INPUT(4), INPUT(6))
 	:   ELSEIF INPUT ISLIKE [IS THE = OF = =] THEN
 	:     ISTHE(INPUT(3), INPUT(5), INPUT(6))
 	:   ELSE [I DO NOT UNDERSTAND YOU]
 	:   CLOSE
 	: END;
.br
And now a final alteration to the system. I would like it to be able
to answer questions of the form:
[DO ANY OF [FRANCE ENGLAND STEVE] HAVE PARIS AS CAPITAL]
expecting the answer:
[YES - FRANCE]
And so:
 	: FUNCTION ANYOF(LIST, VALUE, KEY);
 	:   IF LIST=[] THEN
 	:     "NO"
 	:   ELSEIF REPLYTO([IS THE %KEY% OF %HD(LIST)% %VALUE%]) = "YES"
 	:      THEN [YES - %HD(LIST)%]
 	:   ELSE ANYOF(TL(LIST), VALUE, KEY)
 	:   CLOSE
 	: END;
.br
and the necessary modification to REPLYTO:
 	: FUNCTION REPLYTO(INPUT);
 	:   IF INPUT ISLIKE [WHAT IS THE = OF =] THEN
 	:     WHATIS(INPUT(4), INPUT(6))
 	:   ELSEIF INPUT ISLIKE [THE = OF = IS =] THEN
 	:     THEIS(INPUT(2), INPUT(4), INPUT(6))
 	:   ELSEIF INPUT ISLIKE [IS THE = OF = =] THEN
 	:     ISTHE(INPUT(3), INPUT(5), INPUT(6))
 	:   ELSEIF INPUT ISLIKE [DO ANY OF = HAVE = AS =] THEN
 	:     ANYOF(INPUT(4), INPUT(6), INPUT(8))
 	:   ELSE [I DO NOT UNDERSTAND YOU]
 	:   CLOSE
 	: END;
 	: CONVERSE();
 	: [DO ANY OF [STEVE ENGLAND FRANCE] HAVE PARIS AS CAPITAL]
 	** [YES - FRANCE]
 	: [DO ANY OF [STEVE KENT AARON] HAVE PROGRAMMING AS HOBBY]
 	** NO
 	: [BYE]
 	** [BYE THEN]
 	: TRACE REPLYTO;
 	: CONVERSE();
 	: [THE FATHER OF STEVE IS FRANK]
 	>REPLYTO [THE FATHER OF STEVE IS FRANK]
 	<REPLYTO [OK - I HAVE NOTED THAT FACT]
 	** [OK - I HAVE NOTED THAT FACT]
 	: [DO ANY OF [AARON LONDON STEVE] HAVE FRANK AS FATHER]
 	>REPLYTO [DO ANY OF [AARON LONDON STEVE] HAVE FRANK AS FATHER]
 	!>REPLYTO [IS THE FATHER OF AARON FRANK]
 	!!>REPLYTO [WHAT IS THE FATHER OF AARON]
 	!!<REPLYTO UNDEF
 	!<REPLYTO NO
 	!>REPLYTO [IS THE FATHER OF LONDON FRANK]
 	!!>REPLYTO [WHAT IS THE FATHER OF LONDON]
 	!!<REPLYTO UNDEF
 	!<REPLYTO NO
 	!>REPLYTO [IS THE FATHER OF STEVE FRANK]
 	!!>REPLYTO [WHAT IS THE FATHER OF STEVE]
 	!!<REPLYTO FRANK
 	!<REPLYTO YES
 	<REPLYTO [YES - STEVE]
 	** [YES - STEVE]
 	: [BYE]
 	** [BYE THEN]
 	: UNTRACE REPLYTO;
.br
Undoubtedly, you will not understand all of this program. I advise
you to play with the program by first typing:
 	: LIB CONVERSE;
.br
Use TRACE to explore the program's innards.

You should then attempt to extend the program.
You can get a copy of the program into your directory by giving the command:
 	: %CP  /USR/LIB/CONVERSE.P
Possible extensions are:
.br
(a) Allow inputs of the form:
 	: [A CITY OF ENGLAND IS NEWCASTLE]
.br
or	: [IS BIRMINGHAM A CITY OF ENGLAND]
.br
This is tricky because ENGLAND can have MANY cities and so you will have to
store a list of all cities under the key CITY.
.sp
(b) Give your program some knowledege of the word PART so that it can conduct
the following conversation:
 	: [A PART OF HAND IS FINGER]
 	** [OKAY - I HAVE NOTED THAT FACT
 	: [A PART OF ARM IS HAND]
 	** [OKAY - I HAVE NOTED THAT FACT]
 	: [IS FINGER A PART OF ARM]
 	** YES
.br
.sp
(c) As we allow new types of input sentences so we have to modify REPLYTO.
Write a version of REPLYTO which, instead of knowing directly of the
question answering functions WHATIS etc instead uses a 'question dictionary`.
(Of course, 'question dictionary` is a mis-nomer since not everything we can
say to our program is a question!)
 	: [[WHATIS [WHAT IS THE = OF =]]
 	:  [THEIS [THE = OF = IS =]]
 	:  [ISTHE [IS THE = OF = =]]
 	:  [ISA [IS A = OF = =]]
 	:  [AIS [A = OF = IS =]]
 	:  [WHATHAS [WHAT HAS = AS =]]
 	:  [WHATHAS2 [WHAT HAS = AS = AND = AS =]]]
 	:      -> QUESTIONTYPES;
 : FUNCTION REPLYTO(INPUT, QTYPES);
 	:      IF   QTYPES = []
 	:      THEN [I DO NOT UNDERSTAND YOU]
 	:      ELSEIF ITISTHISTYPE(INPUT, HD(QTYPES))
 	:      THEN DOTHISTYPE(INPUT, HD(QTYPES))
 	:      ELSE REPLYTO(INPUT, TL(QTYPES))
 	:      CLOSE
 	: END;
 	: FUNCTION CONVERSE();
 	:	VARS INPUT;
 	:	LISTREAD() -> INPUT;
 	:	IF INPUT = [BYE] THEN
 	:		[BYE THEN] =>
 	:	ELSE
 	:		REPLYTO(INPUT, QUESTIONTYPES) =>
 	:		CONVERSE();
 	:	CLOSE
 	: END;
.br
.sp
(d) Allow logical connectives, e.g:
 	: [IS EITHER LONDON OR BIRMINGHAM THE CAPITAL OF ENGLAND]
.br
or perhaps:
 	: [IS [LONDON THE CAPITAL OF ENGLAND] AND [28 THE AGE OF STEVE]]
.br
Compare this with:
 	: [DOES ENGLAND HAVE ONE OF [LONDON BIRMINGHAM] AS CAPITAL]
.br
.sp
(e)
A major fault of this program is its very simple understanding of syntax.
This means that the questions put to it have to be in a very specific format.
The recognized formats have 'slots` into which any word can be put.
One way we could extend the program would be to allow a whole list of words to go into
any slot.
Let us say that "==" in a pattern indicates that a whole list can go in at that point.
Using this facility we could change the patterm for ANYOF questions to
 	: [DO ANY OF == HAVE = AS =]
.br
and then ask questions of the form:
 	: [DO ANY OF ENGLAND FRANCE ITALY HAVE LONDON AS CAPITAL]
.br
rather than:
 	: [DO ANY OF [ENGLAND FRANCE ITALY] HAVE LONDON AS CAPITAL]
.br
where we have had to group ENGLAND, FRANCE and ITALY together
with list brackets so that they would fit into one 'slot` in the ANYOF pattern.
.sp
This is quite hard - but well worth thinking about as it leads to even greater
dissatisfaction with this simple template approach to understanding
language.
.sp
Recommended reading
.br
===================
.br
1) The MATCHER demo.
.br
2) The SIR system, written by Betram Raphael, described in
Semantic Information Processing
(ed Minsky - library class = QE 100 Min).
