Path: utzoo!attcan!uunet!husc6!mailrus!uwmcsd1!ig!agate!ucbvax!POLYA.STANFORD.EDU!restivo
From: restivo@POLYA.STANFORD.EDU (Chuck Restivo)
Newsgroups: comp.lang.prolog
Subject: PROLOG DIGEST V6 #28
Message-ID: <8807041642.AA11243@polya.Stanford.EDU>
Date: 4 Jul 88 16:42:22 GMT
Sender: daemon@ucbvax.BERKELEY.EDU
Organization: The Internet
Lines: 1266

Date: Thursday, 5 May 1988 0:1:43-PST
From: Chuck Restivo (The Moderator) 
Reply-to: PROLOG@POLYA.STANFORD.EDU>
US-Mail: P.O. Box 4584, Stanford CA  94305
Subject: PROLOG Digest   V6 #28
To: PROLOG@POLYA.STANFORD.EDU


PROLOG Digest           Friday, 1 Apr 1988      Volume 6 : Issue 28

Today's Topics:
     				Query - Parallel Compilers,     				
		     Implementation - end_of_file & Strings & Eliza
----------------------------------------------------------------------------------------------------------------------------

Date: 23 Mar 88 14:26:19 GMT
From: mcvax!dutrun!dutesta!ignacio@uunet.uu.net  (Ignacio GARCIA ALVES)
Subject: Parallel prolog compilers and interpreters

We are interested in PARALLEL PROLOG COMPILERS AND INTERPRETERS, like for 
example PARLOG and CONCURRENT PROLOG.

But now the problem: WHERE CAN WE FIND THEM?

So we would like to hear the answers to the following questions:
- where can we order them?
- what are the prices?
- who has already such a compiler or interpreter to hear their experience?
- what are the hardware and software requirements?

We do have the following hardware:
- VAX, Berkeley Unix 4.1
- RT, AIX
- PC

All information is welcome either by post or email!

POST:   Ignacio GARCIA ALVES & Mark KORSLOOT
        Delft University of Technology
        Faculty of Electrical Engineering
        Section Computer Architecture
        Mekelweg 4
        2628 CD  DELFT
        THE NETHERLANDS

EMAIL:  !mcvax!dutrun!dutesta!ignacio.uucp
    or  !mcvax!dutrun!dutesta!mark.uucp

-----------------------------

Date: 24 Mar 88 03:06:23 GMT
From: quintus!ok@unix.sri.com  (Richard A. O'Keefe)
Subject:  behavior of read/get0 at end_of_file

I just grepped through the UNIX [UNIX is a trademark of AT&T] manuals,
and all I could find was the function feof(Stream).  None of the UNIX 
utilities I am familiar with uses "eof" to signify end of file.
Franz Lisp does something interesting:
	(ratom [Port [Eof]])
	(read  [Port [Eof]])
	(readc [Port [Eof]])
return the Eof argument (which defaults to nil) when you read the
end of the file, so you can get whatever takes your fancy.

> so i think we could maybe abandon the end_of_file notation of Quintus (sorry
> for you Richard, a compatibility switch could very easily turn it back anyway),

But it ***ISN'T*** a Quintus notation!  This is the notation used by
	DEC-10 Prolog
	EMAS Prolog
	C Prolog
	Quintus Prolog
	Stony Brook Prolog
	ALS Prolog
	Expert Systems International Prolog-2
	AAIS Prolog (in "Edinburgh" mode only)
and doubtless many others.  end_of_file IS the "de facto" standard.
Poterie's suggestions are good ones, but in order to overthrow the
de facto standard, they would have to be MUCH MUCH better, and they
aren't.

> but it is not an important point as the aim would be to discipline one's
> programming style by systematically using the test form: 
> 	eof(Term) 
> and never ever explicit the EOF term itself. Portability is great.

Beware.  While Quintus Prolog offers the library predicate
	is_endfile(?EndMarker)
there are other Prolog systems, such as AAIS Prolog, where there is a
predicate with a similar name which takes a Stream argument:
	is_eof(+Stream)
in AAIS Prolog means "is it the case that Stream is positioned at its end?".
Yes, portability is great, but would it not be more just to reward those
people (such as SICS, Saumya Debray, ALS, and others) who have tried to
provide it, by standardising their solution?

> As a side effect, close/1 is
> not strictly necessary anymore as the following sequence does the job:
> 		eof(EOF), put(EOF)
Um, what about INPUT streams?  And there is another reason for wanting
close/1:  it will close a stream which is not the current output stream.

------------------------------

Date: 26 Mar 88 05:25:23 GMT
From: quintus!ok@unix.sri.com  (Richard A. O'Keefe)
Subject:  End-of-file handling

In article <2403@zyx.UUCP>, bd@zyx.UUCP (Bjorn Danielsson) writes:
> I can't understand why there should be a "strait-jacket solution" to this
> kind of problem. Why not put in enough flexibility to allow for the most
> obvious cases? In Z-Prolog, (which was never designed to be Edinburgh
> compatible) the "read" predicate can handle end-of-file in three different
> ways, depending on an optional argument:
> 	(1) signal an error,
> 	(2) fail,
> 	(3) return a value supplied by the programmer.
> 
Oh, __HOW__ I wish you were were handling the I/O part of BSI substandard!
That's EXACTLY the right sort of attitude for a standard designer.

I think that there are sound technical reasons for regarding fail-at-end
as a straight-out blunder, and I have used PL/I and Fortran enough to
convince me that end-of-file is NOT an error and shouldn't be treated
like one.  (Reading *past* end-of-file is another matter, which is one of
the things I have against fail-at-end.)  But a more powerful operation,
which is not perceptibly harder to implement, which will let me synthesise
the operation I want, and let BIM synthesize the operation they want,
that's exactly the right thing to do in the standard.

Oddly enough, I had intended to mention Algol 68.  Files in Algol 68 are
rather interesting.  One of the things you can do is say

	FILE example file;
	...
	logical file ended OF example file :=
	    (FILE file parameter) BOOL: (
		# return FALSE to get default action (e.g. error report) #
		# return TRUE to say "corrected, please try again" #
		# or jump out #
	    );

Interlisp does the same sort of thing:

	(WHENCLOSE file 'EOF (FUNCTION (LAMBDA (file) (* action *)) ))

So even if we decide to feed this goose instead of killing it, there are
at least three options:
   1.	specify in each call what action to take on end of file
	read(Stream, Term, EofAction)
	-- This affects 'read' only.

   2.	specify per stream what action to take on end of file
	eof_action(Stream, EofAction)
	-- This could affect any form of input.

   3.	allow both.

One of the constraints which is of interest to Quintus is that whatever the
end of file action is, it has to make sense if the stream was being used by
C or Lisp code.  The nice thing about sticking with -1 as the character
end-of-file mark and adopting option 3 is that it does this.

Note though that as I mentioned in my previous message, just because the
host operating system indicates an end-of-file condition doesn't mean that
it isn't possible to read any more from the file.  (UNIX fans will know
about 'tail -f' and why it is useful...)  The standard should, I said in
'84, include a predicate for testing whether the stream is really ended.
If the stream is not really ended, it should be possible to resume reading.

------------------------------

Date: 22 Mar 88 16:55:39 GMT
From: mcvax!unido!ecrcvax!bruno@uunet.uu.net  (Bruno Poterie)
Subject:  behavior of read/get0 at end_of_file

I do not think that having read/1 returning an atom on EOF is a bad thing.
If you take as an example certain UN*X tools, they read their input from
a file (typically stdin) until finding a line composed of a single dot.
So it is perfectly legal to submit a file which contains a dot-line in the
middle if you want only the first part to be feeded to the tool. Same thing
for Prolog, if you have a huge file full of various facts but want only say
the first 100 to be used as input in your test program, then simply add
the EOF mark before line 101. I would then prefer to have it as a directive:
		...
		:- eof.

so that it is not a bare fact but actually a command to the consulting tool
that it have to EOF this input source. It is then coherent with other
directives like:
		...
		:- [file3,file4].
		...
which actually order the consulting tool to insert at this point the content
of the named files. I believe that the notation "eof" is quite standard in
the UN*X system and already in some Prolog, including as a test predicate for
this very term:
		... read(Term), eof(Term) -> ...

so i think we could maybe abandon the end_of_file notation of Quintus (sorry
for you Richard, a compatibility switch could very easily turn it back anyway),
but it is not an important point as the aim would be to discipline one's
programming style by systematically using the test form: 
	eof(Term) 
and never ever explicit the EOF term itself. Portability is great.

	Now for the get0/1 [or get_char/1/2]: having it returning an otherwise
impossible value, say an atom as suggested by Micha, is ok if the returned
thing is an integer representing the ascii code [or ebcdic] of the character.
using the same term as the one returned by read/1, and consequently the same
test predicate as only mechanism to check for EOF, would greatly improve the
compactness and consistency of the i/o system. As a side effect, close/1 is
not strictly necessary anymore as the following sequence does the job:
		eof(EOF), put(EOF)
Because obviously put/1 must handle the same term in the same way (I am afraid
that outputing CHAR modulo 256 would not work in this case).
I nevertheless believe that EOF == -1 is a clearer convention, returning an
object of the same type but out of the normal range of normal input, and
is already the UN*X convention. It would not force put/1 to accept it as EOF
character, as it would be outputed as: -1 modulo 256 (or 512) == 255. Passing
-1 to UN*X putchar() does not generate an EOF!

Ok, enough delirium tremens for today. My main point is: the character i/o
should provide a very low level facility, with no hypothesis about the use
which could be made of them. Using read(Term) and eof(Term) provides an
uniform, simple, elegant and portable mean of performing i/o at Prolog term
level. Using get0/1 implies you are interested in the real bits contained
in your input support, so you want to control it at a low level. Returning
the -1 value is portable and low-level, because independant of ascii or
any other character set. Alternatively, returning eof and using the same
eof(Char) test predicate would be again low-level, portable, and free of
any supposed semantic. More important, most of prolog input loops may be
adapted with this scheme at low cost. Failing at EOF, however, would mean
full rewriting of those applications and system libraries.

------------------------------

Date: 22 Mar 88 10:29:39 GMT
From: mcvax!prlb2!kulcs!bimandre@uunet.uu.net  (Andre Marien)
Subject: end_of_file treatment in prolog

Sorry for being inaccurate: we should have taken the trouble of explaining what
BIMprolog's predicate eof/0 does and it is different from what Richard's
at_eof/0 does:
eof/0 succeeds only after a read/1 or get0/1 has failed because end of
file was encountered
so, let me change buggy_read into:

        non_buggy_read(Term) :- bim_read(Term), ! .
        non_buggy_read(end_of_file) :- eof .

actually, eof/0 is written using a predicate in_status/1 which unifies
its argument with an integer indicating the 'status' of the input stream

> end-failure approach, despite having asked Bart Demoen at the Prolog
> Benchmarking Workshop for enlightenment on this point.  Again, this

Perhaps you asked Andre' Marien, but not Bart Demoen: he was not at the Prolog
Benchmarking Workshop

Now about the transformation of

        s1: a -> s2.
        s1: b -> s1.
        s1: $  -> accept.

to a prolog procedure;

1. the textbook uses an explicit end of file marker, so it is clear that the
   behavior of Cprolog's get0/1 suites better in this transformation;
   still, I would rather represent a state by a predicate with arity 0 and let
   it get its character itself:
   
        s1 :- get0(X) ,
        	(X = a , s2 ;
        	 X = b , s1 ;
        	 X = -1 , true
        	) .

      this of course with get0/1 behaving like in Cprolog

2. Now, do it with BIMprolog's get0/1, I will call it BIMget0 so that you will
   always know which behavior to expect

   s1 :- BIMget0(X) , (X = a , s2 ;
        	       X = b , s1
        	      ) .
   s1 :- eof .
   
   this doesn't look very nice, but
   
	a. I would expect that in a parser, in most states eof indicates an
	   error, so that error recovery must be done, which is very easy
	   (in prolog) by backtracking to the appropriate level (state) in the
	   parser; so in most states, there would be no clause like s1 :- eof.
	   then, on eof, s1 fails as it does on input characters different
	   from a or b

	   see Pascal : an endpoint is the endmarker, and occurrence of eof
	                    during tokenizing means an error
	   see Prolog : every clause must have an endpoint; eof could only
	                    occur after a clause

	b. prolog (at least BIMprolog) is used for other things than writing
	   tokenizers - actually, tokenizers should not be written at all: they
	   should be generated - and in an administrative application written in
	   prolog, using BIMread or BIMget, the programmer can feel at ease:
	   after a successful BIMread or BIMget, he has got data; it is in a
	   way impossible for him to forget testing for eof, because otherwise
	   the execution of the program would never have gone so far

3. A more general approach can be used, using a state transition table.
   The previous program can be derived from this by partial evaluation.
   In the triangle puzzle problem discussion, R.O'K argued that this
   approach is more general.

	step_state(accept) :- ! .
	step_state(From) :- BIMget0(X), !,
			From : X => New,
			step_state(New) .
	step_state(From) :- BIMeof,
			From : '$' => New,
			step_state(New) .

	s1: a => s2.
	s1: b => s1.
	s1: '$' => accept.

	If eof is to be treated as an error, remove the last clause of
	step_state/1.
	Eof is handled in one place, in Cprolog one should add :

		transit(si,-1,error) .

	for all states, or write :

		state(From) :-	get0(X), noteof(X), !,
				transit(From,X,New),
				state(New) .

		noteof(-1) :- !, fail .
		noteof(_) .
 
To sum up: there is an appropriate level of testing for eof: BIMread and BIMget
(and BSIread and BSIget for that matter, to answer one of Richard's questions
and worries) allow you to do this testing of eof at these levels only, while
read and get0 of Cprolog force you to test it after every input operation

------------------------------

Date: 23 Mar 88 09:54:10 GMT
From: quintus!ok@unix.sri.com  (Richard A. O'Keefe)
Subject:  behavior of read/get0 at end_of_file

In article <518@ecrcvax.UUCP>, micha@ecrcvax.UUCP (Micha Meier) writes:
> 	By the way, get0/1 does *not* exist in BSI, it uses get_char/1 instead,
> 	and its argument is a character, i.e. a string of length 1.
> 	This means that the type 'character' is inferred from
> 	the type 'string' (and not the other way round like in C).
> 	Does anybody out there know what advantages this can bring?
> 	It is independent on the character <-> integer encoding,
> 	but this only because explicit conversion predicates have
> 	to be called all the time.

I find it extremely odd to call a string of length one a character.
It's like calling a list of integers which contains one element an
integer.  Do we call an array with one element a scalar?

I haven't commented on the BSI's get_char/1 before because for once they
have given a new operation a new name.  There are two problems with it.
A minor problem is that the result being a string, they can't represent
end of file with an additional character, so the fail-at-end approach is
hard to avoid.  (Not impossible.)  There is an efficiency problem:
something which returns an integer or a character constant can just
return a single tagged item, but something which returns a string either
has to construct a new string every time, or else cache the strings somehow.

For example, Interlisp has a function which returns you the next character
in the current input stream, represented as an atom with one character in
its name.  (Well, almost:  characters `0`..`9` are represented by integers
0..9.)  This was quite attractive on a DEC-20, where you could just compute
a table of 128 atoms once and for all.  It wasn't too bad on VAXen either,
where the table had to have 256 elements.  But it because rather more
clumsy on the D machines, which have a 16-bit character set.  (Can you say
"Kanji"?  I knew you could.)  So the alternatives I can see at the moment
are
    o	construct a new string every time.
    o	precompute 2^16 strings.
    o	cache 2^8 strings, and construct a new string every
	time for Kanji and other non-Latin alphabets.
    o	not support Kanji or other non-Latin alphabets at all.
(Can you say "Cyrillic"?  How about "Devanagari"?  You may need the
assistance of a good dictionary; I used to mispronounce "Devanagari",
and probably still do.)

I wrote that
> >For example, the arcs
> >	s1: a -> s2.
> >	s1: b -> s1.
> >	s1: $  -> accept.
> >would be coded like this:
> >	s1(0'a) :- get0(Next), s2(Next).
> >	s1(0'b) :- get0(Next), s1(Next).
> >	s1(- 1) :- true.
Meier says that
> 	In his tutorial to the SLP '87 Richard has taken another
> 	representation of a finite automaton which is more appropriate:
> 	s1 :-
> 		get0(Char),
> 		s1(Char).
> 
> 	s1(0'a) :-
> 		s2.
> 	s1(0'b) :-
> 		s1.
> 	s1(-1) :-
> 		accept.
There wasn't time to go into this in detail in the tutorial, but it
should be obvious that the first approach is more general:  in particular
it can handle transitions where (perhaps because of context) no input is
consumed, and it can handle lookahead.
>	Such representation can
> 	be more easily converted to the BSI's variant of get:
> 	s1 :-
> 		% do the corresponding action
> 		( get0(Char) -> s1(Char)
> 		;
> 		  accept
> 		).
This doesn't generalise as well as the end-marker version.
Here is the kind of thing one is constantly doing:

	rest_identifier(Char, [Char|Chars], After) :-
		is_csymf(Char),
		!,
		get0(Next),
		rest_identifier(Next, Chars, After).
	rest_identifier(After, [], After).

See how this code can treat the end marker just like any other
character:  because it doesn't pass the is_csymf/1 test (copied from
Harbison & Steele, by the way) we'll pick the second clause, and there
is no special case needed for an identifier which happens to be at the
end of a stream.

The fail-at-end approach forces us not only to do something special
with the get0/1 in rest_identifier/3, but in everything that calls it.
(In the Prolog tokeniser, there are two such callers.)

The point is that if-then-elses such as Meier suggests start
appearing all over the place like maggots in a corpse if you adopt
the fail-at-end approach, to the point of obscuring the underlying
automaton.

> 	I must say, none of the two seems to me satisfactory. Richard's
> 	version is not portable due to the -1 as eof character.

If the standard were to rule that -1 was the end of file character,
it would be precisely as portable as anything else in the standard!
In strict point of fact, the Prolog-in-Prolog tokeniser was written
in DEC-10 Prolog for DEC-10 Prolog, and used 26 as the end of file
character, and 31 as the end of line character.  It took 5 minutes
with an editor to adapt it to Quintus Prolog.  I wish C programs
written for UNIX took this little effort to port!

> 	for a Prolog system it is better to have get0/1 return
> 	some *portable* eof (e.g the atom end_of_file, for get0/1
> 	there can be no confusion with source items) instead of
> 	some integer.

It is important that the end-of-file marker, whatever it is, should be
the same kind of thing, in some sense, as the normal values, so that
classification tests such as is_lower/1, is_digit/1, and so on will
just fail quietly for the end-of-file marker, not report errors.  Since
end of file is rare, we would like to test the other cases first.
Pop-2 on the Dec-10 returned integers almost all the time, except that
at the end of a stream you got an end-of-file object which belonged to
another data type (there was only one element of that data type, and it
printed as ^Z).  This was in practice a major nuisance, because before
you could do anything other than an equality test with the result, you
had to check whether it was the end of file mark.

I have been giving out copies of the Prolog-in-Prolog tokeniser to show
how easy it is to program character input with the Edinburgh Prolog
approach.  If someone would give me a tokeniser for BSI Prolog written
entirely in BSI Prolog using the fail-at-end approach, and if that
tokeniser were about as readable as the Prolog-in-Prolog one, that would
go a long way towards convincing me that fail-at-end was a good idea.

> 	BSI objects that if [read/1] returns e.g. the atom end_of_file
> 	then any occurrence of this atom in the source file
> 	could not be distinguished from a real end of file.

That's not a bug, it's a feature!  I'm serious about that.  At Edinburgh,
I had the problem that if someone asked me for help with Prolog, they
might be using one of four different operating systems, where the end
of file key might be
	^Z
or	^D
or	^Y
or	something else which I have been glad to forget.
No problem.  I could always type
	end_of_file.
to a Prolog listener, and it would go away.  Oh, this was so nice!
In fact, on my SUN right now I have function key F5 bound to
"end_of_file.\n" so that I can get out of Prolog without running the
risk of typing too many of them and logging out.

Another thing it is useful for is leaving test data in a source file.
One can do
	
	
	end_of_file.
	
and include the test cases in the program or not just by moving the
end_of_file around.

Ah, you'll say, but that's what nested comments are for!
Well no, they don't work.  That's right, "#| ... |#" is NOT a reliable
way of commenting code out in Common Lisp, and "/* ... */" is NOT a
reliable way of commenting code out in PopLog.  But end_of_file, in
Edinburgh Prolog, IS a reliable way of commenting out the rest of the file.

> 	In this case, a remedy would be the introduction of

Prolog needs a remedy for end_of_file like Elizabeth Schwarzkopf
needs a remedy for her voice.

Before taking end_of_file away from me, the BSI committee should supply
me with a portable way of exiting a break level and a reliable method of
leaving test cases in a file without having them always read.

------------------------------

Date: 24 Mar 88 08:58:35 GMT
From: quintus!ok@unix.sri.com  (Richard A. O'Keefe)
Subject: behavior of read/get0 at end_of_file

Just to continue with the get0 topic:

	The fail-at-end approach rests on an assumption
	which deserves to be made explicit, because it is false.

What is the assumption?   That receiving the end-of-file indication from
an operating system indicates that there is nothing further to read in
that stream.  This is false?  Yes.

Let's ignore 4.2BSD sockets, V.3 Streams, non-blocking I/O, VMS
concatenated files, and other esoterica which one doesn't expect
BSI Prolog to cope with.  Let's just consider terminals.

In Tops-10 (home of DEC-10 Prolog):
	end-of-file from the terminal is a software convention (^Z).
	You can just keep reading from the terminal after that, and
	in fact that's exactly what DEC-10 Prolog does.

In UNIX (original home of C Prolog):
	end-of-file from the terminal is a software convention
	(EOF character typed after an empty line).
	You can just keep reading from the terminal after that, and
	in fact that's exactly what C Prolog does.

In VM/CMS, using SAS Lattice C
	end-of-file from the terminal is a software convention
	(some magic string, which defaults to "EOF", but it is
	trivially easy for a program to change it -- use afreopen()).
	I believe that you can keep reading from the terminal after
	that, but I haven't tried it myself.

On a Macintosh, using ALS Prolog
	end-of-file from a window is a software convention
	(you click on "End of File" in the "Edit" menu).
	All windows and streams remain live after that, and
	you can just keep reading, and that's what ALS Prolog does.

On a Xerox Lisp Machine, using Xerox Quintus Prolog
	end-of-file from a TEXEC window is a software convention.
	All windows and streams remain live after that, and
	you can just keep reading, and that's what XQP does.

[The sample of Prologs is not of interest here; my point is that there
 are several *operating systems* with this characteristic.
]
So the rule actually followed in Edinburgh-compatible Prologs is that
    -   the sequence of characters codes returned by get0/1 is
	the sequence of characters delivered by the source
    -   with the end-of-file marker inserted every time the
	host indicated the end-of-file condition
    -	Prolog receives through get0/1 as many characters and as many
	end-of-file markers as there are; any attempt to read past the
	end of this union stream is an error.  Not a failure, an error.

It happens that when you are reading from disc files, most operating
systems will indicate the end of file condition once.

Are terminals the only kind of file for which multiple end-of-file
conditions are plausible?  No.  The convention for tapes is that a
single tape-mark (usually reported as an end-of-file condition) is
merely a separator; a tape as such is terminated by a double tape-mark.
Thus a Prolog program copying one tape to another (this is a reason why
we might want put(-1) NOT to close a file; if it does anything special
on a tape it should be to write a tape-mark) might want to keep reading
after seeing an end-marker.

------------------------------

Date: 24 Mar 88 15:29:47 GMT
From: mcvax!ukc!dcl-cs!simon@uunet.uu.net  (Simon Brooke)
Subject:  Strings

In article <776@cresswell.quintus.UUCP> ok@quintus.UUCP (Richard A. O'Keefe) writes:
>Xerox Lisp uses lists for bignums to this very day.

Yes, true. But please don't assume this means it is efficient. For
example, I recently benchmarked a Xerox 1186 running Interlisp (Koto)
against the new Acorn Archimedes running Arthur Norman's Cambridge Lisp.
Generally the Arch was a bit faster, reflecting the simpler lisp and
faster processor. But running (fact 1000) it was 321 (three hundred and
twenty one) times faster - and this must reflect grotesque inefficiency in
Xerox' bignum code.

------------------------------

Date: 24 Mar 88 18:35:05 GMT
From: eagle!icdoc!doc.ic.ac.uk!cdsm@ucbvax.Berkeley.EDU  (Chris Moss)
Subject:  Strings

I haven't contributed to the string discussion because I don't believe I
know all the answers. But I do have strong reservations about the current
Edinburgh implementations. Let me air them:

1. They don't get printed properly by "write". I don't like seeing
"Prolog" come out as [80,114,111,108,111,103]. OK, I can define a
"writestring", but not use it within a bigger structure without pain.  

Now here's an odd thing: there is no writestring routine in the Quintus
library that I can discover! Not in the built-ins or the (extensive)
library!  

What does that tell us about the use of strings? It suggests to
me that people actually always use atoms for this purpose, and somewhere 
in their code is an implicit definition: 
    writestring(String) :- name(Atom,String),write(Atom).  

Note that in most systems this involves an entry in the symbol table 
(reasonably slow) and a limit of 256 (or maybe 512) characters.

MuProlog DOES print out "Prolog" as a string, wherever it occurs.
Presumably it has to scan the list first. It uses list format whenever
there is a integer outside the range 32-127 in the list (it also gets tabs
right tho I don't understand why it finishes at 127 not 126). That's a
pretty good heuristic, tho I can imagine it going wrong on occasions. e.g.
if I read in a clause over several lines the string will contain newlines,
so my DCG debugging will still look horrid!

2. I don't like having ASCII values in my programs. With a certain amount
of discipline and inefficiency one can get round that, by saying, for 
instance:
    lowercase(X) :- "a"=[A], "z"=[Z], X>=A, X=, cdsm@ivax.doc.ic.ac.uk (Chris Moss) writes:
> Richard's certainty that the
> Prolog world begins and ends with Edinburgh is not shared in France!

I have no such certainty.  I think the Prolog world includes Israel,
Japan, Australia, Portugal, the USA, Hungary, China[*], and all sorts of
places.  The plain fact of the matter is that there are two sorts of
Prolog systems:  ones whose authors have tried to provide some sort of
compatibility with other Prologs, and ones whose authors have let their
imaginations rip.  What the compatible Prologs are compatible with is
not Prolog-II or Waterloo Prolog, but Clocksin&Mellish.

[*] I am credibly informed that the PRC uses an unauthorised translation
    of Clocksin & Mellish as the main Prolog text, so presumably the
    French Prologs are of small concern to them.

------------------------------

Date: 20 Mar 88 20:47:39 GMT
From: lagache@violet.Berkeley.EDU  (Edouard Lagache)
Subject: An Eliza-like problem solving assistant (400+ lines).

    The following program is the mockup of the Eliza-like problem solving
    assistant that was demonstrated at the March 10 meeting of the PROLOG Forum.
    The program is the result of 3 late night work, and NO claims of
    correctness or efficiency are expressed on implied.  On the contrary,
    suggestions for expansion or improvement are most welcome.  As stated
    earlier, it is hoped to turn this program into a group project, so
    we hope that there is plenty of room for expansion!

    There are some calls to my imfamous PROLOG libraries.  The 'window'
    and 'set_attribute' calls will only work on a Texas Instruments PC,
    so I leave it to you to omit, or port these calls to your system.
    There is one call to the predicate 'nthelem', I have enclosed a copy
    of the predicate at the end of the file.  There may be other calls
    that I have forgotten, if so let me know and I will provide the needed
    code.

    Hack to your hearts delight!

    -- Edouard Lagache

/* File: 'consulta.pro' */
/******************************************************************************/
/*                                                                            */
/*                  An A.I. Consultant for PROLOG Programmers                 */
/*                                                                            */
/*       This program is intended to serve as a "intelligent consultant"      */
/*  for PROLOG programs to turn to when encountering some impasse in a        */
/*  programming project.  The program is based on the "Eliza" program, but    */
/*  it designed to provide comments that might foster the user to "solve"     */
/*  his/her own problem.                                                      */
/*                                                                            */
/*               A collaborative project of the PROLOG Forum.                 */
/*    Release - 1.00,  March - 1988,  Copyright(C) 1988, The PROLOG Forum     */
/*                                                                            */
/*  Credits: The concept of an "Eliza" type program to help users with        */
/*           problem solving was first proposed (as far as we know) by        */
/*           Professor Charles Woodson of the School of Education at          */
/*           U.C. Berkeley.  He also built a small system in LISP for the     */
/*           purposes of demonstrating the concept to members of his LISP     */
/*           programming course.                                              */
/*                                                                            */
/******************************************************************************/

/*>> 'gethelp' is the routine that the user can call to get access to the <<*/
/*>> system.  E. Lagache, Ver-1.0, 3/88                                   <<*/
gethelp :- tell('_WINDOWS'), make_window(0,15,14,64), set_attribute(magenta),
           clear_screen, set_attribute(white),
           prtstr("Please describe your problem, type 'quit.' to resume work"),
           nl, converse, close_window, tell(user).

/*>> 'converse' is the main "looping" routine.  It takes a list of words <<*/
/*>> and searches for keywords.  Then it generates a response based on   <<*/
/*>> keywords.  The responses are randomized to make the system more     <<*/
/*>> "humanlike".   E.L.,  Ver-1.0, 3/88                                 <<*/
converse :- set_attribute(white), prtstr("<>-> "), set_attribute('yellow'),
            read_sentence(Request), nl, remove_duplicates(Request,Request1),
            make_reply(Request1,Reply), set_attribute(cyan),
            print_reply(Reply), nl, continue(Request1).

/* continue makes the recursive call to 'converse' if the user doesn't want */
/* quit */
continue(['quit','.']) :- set_attribute(white), pause.
continue(_) :- converse.

/* 'pause' uses the interval function to simply carry out some time consuming */
/* task to make a delay between the exit prompt, and the closing of the       */
/* window.                                                                    */
pause :- interval(1,X,10), fail.
pause.

/* 'print_reply' simply prints out the list of items recursively */
print_reply([]).
print_reply([Item|Rest]) :- print(Item), put(32), print_reply(Rest).


/*>>------------------------------------------------------------------------<<*/
/*>> 'make_reply' generates a response to a keyword in the users input text <<*/
/*>> it keeps a list of keywords and appropriate responses, and chooses one <<*/
/*>> randomly to provide a "natural" appearance.                            <<*/
/*>> E. Lagache,  Ver - 1.0, 3/88                                           <<*/
/* Exit comment */
make_reply(['quit','.'],['Thank','you','I','hope','these','comments','were',
                         'useful.']
          ).

/* Main test, select some set of keywords, test if they match, if so succeed */
/* otherwise fail and look at another possibility.                           */
make_reply(Sentence,Reply) :- word_class(Keywords,Responses),
                              intersection(Keywords,Sentence,List),
                              List \== [], !, length(Responses,Top),
                              irandom(1,Top,Number),
                              nthelem(Responses,Number,Reply).

/* if no keywords are found, then use default responses */
make_reply(_,Reply) :- default_responses(Responses), length(Responses,Top),
                       irandom(1,Top,Number), nthelem(Responses,Number,Reply).

/* 'remove_duplicates' returns a list that contains only one of each item.  */
/* This is necessary for the 'intersection' predicate, and makes the search */
/* less time consuming.  E.L. 3/88                                          */
remove_duplicates([],[]).
                             /* First case, don't copy duplicate items */
remove_duplicates([Item|Rest],NewRest) :- member(Item,Rest), !,
                                          remove_duplicates(Rest,NewRest).
                             /* If not duplicated, then copy */
remove_duplicates([Item|Rest],[Item|NewRest]):- remove_duplicates(Rest,NewRest).

/* 'intersection' return true if one of the keywords was found among the */
/* words typed in.  From C&M page 154 */
intersection([],X,[]).
intersection([X|R],Y,[X|Z]) :- member(X,Y), !, intersection(R,Y,Z).
intersection([X|R],Y,Z) :- intersection(R,Y,Z).

/*>> 'irandom' returns a integer in the range specified by the first two <<*/
/*>> arguments to the predicate, E. Lagache, Ver-1.0, 3/88               <<*/
/*>> Values for computation taken from the first edition of "Oh Pascal"  <<*/
/*>> By Clancy and Cooper (page-227).                                    <<*/
irandom(Lower,Upper,Number) :- get_seed(Seed),
                               Start is (25173*Seed + 13849) mod 65536,
                               Number is (Start mod (Upper - Lower)) + Lower.

/* 'get_seed' will in general be an implementation specific predicate.     */
/* For ADA PROLOG, 'get_seed' will return the number of logical inferences */
/* so far made, using the ADA specific 'licount' predicate                 */
get_seed(Seed) :- licount(Seed).



/*>>---------------------------------------------------------------------<<*/
/*>> 'read_sentence' is a routine to take user input and make a list of  <<*/
/*>> atoms out of it.  It is slightly modified from Clocksin & Mellish   <<*/
/*>> page 104.                                                           <<*/
read_sentence([W|Wa]) :- get0(C), readword(C,W,C1), restsent(W,C1,Wa).

/* Given a word and the character after it, read in the rest of the sentence */
restsent(W,_,[]) :- lastword(W),!.

restsent(W,C,[W1|Wa]) :- readword(C,W1,C1), restsent(W1,C1,Wa).

/* Read in a single word, given initial character, and remembering what */
/* character came after that word.                                      */
readword(C,W,C1) :- single_character(C), !, name(W,[C]), get0(C1).

readword(C,W,C2) :- in_word(C,NewC), !, get0(C1), restword(C1,Cs,C2),
                    name(W,[NewC|Cs]).

readword(C,W,C2) :- get0(C1), readword(C1,W,C2).

/* continue process of stringing characters of the same word together */
restword(C,[NewC|Cs],C2) :- in_word(C,NewC), !, get0(C1), restword(C1,Cs,C2).

restword(C,[],C).

/*  These characters form words on their own */
single_character(44).        /* , */
single_character(59).        /* ; */
single_character(58).        /* : */
single_character(63).        /* ? */
single_character(33).        /* ! */
single_character(46).        /* . */

/* These characters can appear within a word */
/* the second in_word clause converts letters to lower case */
in_word(C,C) :- C>96, C<123.                /* 'a' to 'z' */
in_word(C,L) :- C>64, C<91, L is C+32.      /* 'a' to 'z' */
in_word(C,C) :- C>47, C<58.                 /* '0' to '9' */
in_word(39,39).                             /* '\'' */
in_word(45,45).                             /* '-' */

/* These words terminate a sentence */
lastword('.').
lastword('!').
lastword('?').

/*>>-----------------------------------------------------------------------<<*/

/*>> 'word_class' and 'default_responses' contain the text that will be    <<*/
/*>> presented to the user.  'word_class' also contains the list of        <<*/
/*>> keywords, that will be used to decide if this type of response is     <<*/
/*>> appropriate.                                                          <<*/

/* --------------------- Programming specific keywords -------------------- */
/* Words related to failure to get the file to consult properly */
word_class([consult,consulting,compile,compiling,load,loads,loading],
           [
              ['Do',you,know,at,what,location,the,problem,'is?'],
              ['Could',the,problem,be,at,a,different,place,from,where,the,
               error,message,is,'reported?'
              ],
              ['What',sort,of,diagnostic,message,did,the,system,'give?'],
              ['What',sort,of,problems,could,have,caused,this,'situation?'],
              ['Is',there,another,way,you,could,arrange,your,file,that,might,
               make,it,easier,to,find,the,'problem?'],
           ]
          ).

/* Problems with incorrect output */
word_class([output,print,prints,printing,write,writes,writing,printout],
           [
              ['What',sort,of,output,were,you,'expecting?'],
              ['What',kind,of,output,did,you,actually,get,'(if','anything)?'],
              ['How',could,you,modify,your,program,to,get,more,information,
               about,this,'i/o','problem?'
              ],
              ['Can',you,see,where,in,the,program,the,bug,must,be,'located?'],
              ['How',could,you,further,localize,the,source,of,the,incorrect,
               'output?'
              ]
           ]
          ).

/* Input problems */
word_class([input,read,reads,reading],
           [
              ['How',do,you,that,the,problem,is,caused,by,incorrect,input,
               'routines?'
              ],
              ['How',could,you,modify,your,program,to,get,more,information,
               about,this,'i/o','problem?'
              ],
              ['How',could,you,further,localize,the,source,of,the,incorrect,
               input,'behavior?'
              ],
              ['How',could,you,test,these,input,routines,in,'isolation?'],
           ]
          ).

/* flow of control problems */
word_class([infinite,loop,recursion,stepping,trace,tracing],
           [
              ['Where',is,the,last,place,that,you,know,your,program,was,running,
               'correctly?'
              ],
              ['How',do,you,know,that,the,program,is,not,behaving,as,it,
               'should?'
              ],
              ['Which',predicates,could,have,caused,this,'phenomenon?'],
              ['Is',there,any,way,that,you,could,isolate,the,predicates,that,
               are,at,'fault?'
              ],
           ]
         ).
/* Error word */
word_class([error,warning,failure,diagnostics],
           [
              ['Can',you,isolate,the,error,to,one,'place?'],
              ['Can',you,find,a,reasonable,interpretation,for,these,
               diagnostics
              ],
              ['Have',you,encountered,similar,sorts,of,messages,in,the,
               'past?'
              ],
              ['What',sort,of,information,would,allow,you,to,deal,with,this,
               'message?'
              ],
           ]
          ).

/* Logical errors */
word_class([infer,inference,deduce,deduction,entails,entailment],
           [
              ['What',should,have,the,system,'deduced?'],
              ['What',was,the,last,inference,that,you,know,was,'correct?'],
              ['How',do,you,know,that,the,inference,was,in,fact,incorrect,
               '(given',the,systems,actual,'configuration)?'
              ],
              ['What',sort,of,database,condition,could,have,caused,the,
               observed,'deductions?'
              ]
           ]
          ).

/* Bug talk */
word_class([bug,malfunction,wrong,incorrect,incomplete,unexpected],
           [
              ['How',do,you,know,you,have,a,'bug?'],
              ['Where',is,the,last,place,that,you,know,your,program,was,
               functioning,'correctly?'
              ],
              ['What',program,behavior,where,you,'expecting?'],
              ['What',indicates,that,something,is,'wrong?'],
              ['What',occurred,that,was,not,'expected?'],
              ['What',information,would,you,need,to,isolate,the,'bug?'],
              ['what',would,you,need,to,know,to,locate,the,'malfunction?'],
              ['What',tests,could,you,perform,to,get,at,the,'problem?'],
           ]
          ).

/* -----------------  Problem solving keywords  ------------------------- */
/* Alternatives */
word_class([option,options,alternatives,possible,possibilities],
           [
              ['What',alternatives,have,you,already,'tried?'],
              ['Are',there,other,possibilities,that,you,might,'consider?'],
              ['Which',options,have,you,already,'tried?'],
              ['Might','I',suggest,that,you,take,a,new,look,at,your,
               'alternatives.'
              ],
           ]
          ).
/* Reflection, on previous work */
word_class([tried,attempted,thought],
           [
              ['What',have,you,done,in,the,past,in,such,'situations?'],
              ['Tell',me,which,approaches,you,have,already,'tried?'],
              ['What',have,you,attempted,'previously?'],
              ['What',have,you,done,here,that,you,might,want,to,do,differently,
               in,the,'future?'
              ]
           ]
          ).

/* Strategic problem solving, evaluating plans */
word_class([try,attempt,do,complete,investigate,think,thinking],
           [
              ['How',will,trying,this,help,you,out,of,your,'impasse?'],
              ['What',can,you,learn,from,doing,'this?'],
              ['Are',you,sure,that,there,is,not,a,more,productive,investigation,
               that,you,could,'make?'
              ],
              ['Are',there,better,ways,to,get,the,information,you,need,to,solve,
               this,'problem?'
              ],
           ]
          ).

/* -------------------------- Human Psychology words ----------------------- */
/* stuck words */
word_class([stuck,stopped,impasse],
           [
              ['Perhaps',you,should,reflect,back,on,the,various,'possibilities.'
              ],
              ['There',must,be,alternatives,that,you,have,not,'considered.'],
              ['I',suggest,that,you,step,back,from,the,problem,and,review,
               what,you,have,'tried.'
              ],
           ]
          ).
/* "down" words */
word_class([depress,depressing,depressed,frustrating,frustrated,mad,annoy,
            annoyed,annoying
           ],
           [
              ['It',is,not,a,good,idea,to,get,worked,up,about,a,'program.'],
              ['One',should,not,take,this,too,'seriously,',after,all,it,is,
               only,a,'program.'
              ],
              ['If',this,task,is,getting,to,you,perhaps,you,should,take,a,break,
               and,come,back,to,it,'later.'
              ],
              ['Perhaps',you,have,been,working,too,long,on,this,one,'problem,',
               maybe,you,should,take,a,'break.'
              ],
              ['Do',you,tell,me,that,this,program,is,getting,to,'you!'],
              ['Well',nobody,said,that,programming,was,'easy.'],
              ['Do',not,get,worked,up,over,'this,','after,all,',no,program,that,
               is,interesting,is,easy,to,'write.'
              ]
           ]
         ).

/* "Bad" words (should this sort of thing be censored!?!) */
word_class([fuck,fucking,shit,shitty,damn,screw,screwed,hell],
           [
              ['Well','I',hope,that,relieves,your,'aggressions;',now,can,
               we,get,back,to,'work?'
              ],
              ['I',am,glad,you,know,'profanity;',unfortunately,this,system,
               only,understands,'PROLOG!'
              ],
              ['Yes,','yes,',profanity,is,the,language,that,all,programmers,
               know,'best!'
              ],
              ['If',you,can,not,say,anything,more,'constructive,',perhaps,
               you,should,take,a,break,and,come,back,to,this,'problem.'
              ],
              ['Sorry',that,language,is,not,in,my,'vocabulary!'],
           ]
         ).

/*  Help words */
word_class([help,assistance,explain,explanation],
           [
              ['In',what,area,do,you,need,some,'assistance?'],
              ['Can',you,be,more,specific,about,what,sort,of,help,you,'need?'],
              ['Exactly',what,sort,of,information,do,you,'seek?'],
              ['In',what,way,may,'I',be,of,'assistance?'],
              ['What',kind,of,advice,do,you,'need?'],
              ['Exactly',in,what,area,do,you,need,an,'explanation?'],
              ['Please',tell,me,background,about,your,'problem?'],
              ['Could',you,further,elaborate,on,the,type,of,problem,you,are,
               'having? '
              ]
           ]
          ).

default_responses([
                   ['Please','continue.'],
                   ['Could','you','say','more','about','your','problem.'],
                   ['I','would','like','to','hear','some','more','specifics.'],
                   ['Could','you','please','elaborate','on','one','aspect',
                    'of','your','problem.'
                   ],
                   ['Is',there,anything,else,that,you,know,about,this,
                    'problem?'
                   ],
                   ['I',do,not,quite,understand,the,'situation;',could,you,
                    elaborate,it,for,'me?'
                   ]
                  ]
                 ).

/* * * * * * * * * * * * * * * * * * * * * * * */
/*  'nthelem' returns the element in the       */
/*  'Index' place of the list.                 */
/*                                             */
/*     E. Lagache,  Vers. - 1.00, Dec - 86     */
/* * * * * * * * * * * * * * * * * * * * * * * */
nthelem([],_,error):- !, fail.  /* fail if list is smaller than index */
nthelem([Item|_],1,Item):- !.   /* return item */
nthelem([X|Rest],NewIndex,Item):- Index is NewIndex-1,nthelem(Rest,Index,Item).

/* end file consulta.pro */

------------------------------

Date: 24 Mar 88 02:37:00 GMT
From: quintus!ok@unix.sri.com  (Richard A. O'Keefe)
Subject:  An Eliza-like problem solving assistant (400+ lines).

In article <7845@agate.BERKELEY.EDU>,
lagache@violet.berkeley.edu (Edouard Lagache) distributed
an "Eliza-like problem solving assistant".

(1) In the tables at the end of the program, the pattern
	word_class([...],
	    [
		[...],
		...
		[...],
*--------------------^
	    ]).
    occurs quite often.  While C allows extra commas in initialisers,
    many Prolog systems will be unhappy about that extra comma.

(2) In order to test whether any of a given list of Keywords occurs in
    a given Sentence, the program does

	intersection(Keywords, Sentence, List),
	List \== [],

    This is correct, but there isn't a lot of point in constructing List.
    A more efficient approach (effiency is not a concern in this program)
    would be to call

	intersect(Keywords, Sentence)

    where for example

	intersect(Set1, Set2) :-
		member(X, Set1),
		member(X, Set2),
		!.

(3) I thought I'd try the program.  This is what I got:
Please describe your problem.  Type 'quit.' to resume work.
e> My nematodes have been eaten by the peripatus.
Could you say more about your problem. 
e> Yes.
Could you please elaborate on one aspect of your problem. 
e> Well, the peripatus crawled out of its box last night, and it got into
e> another box on the same bench, where we keep nematodes, and well, it
e> ate the lot of them.
Is there anything else that you know about this problem? 
e> I suppose there are two problems, really.  One problem is how do I
e> stop this sort of thing in the future, and the other is where do I
e> get some more nematodes right now.  I want your help with the second
e> part of the problem.
What can you learn from doing this? 
e> From doing WHAT?
Could you say more about your problem. 
e> I just told you everything there was to say.
Could you say more about your problem. 
e> You're getting a bit repetitious, aren't you?
I do not quite understand the situation; could you elaborate it for me? 
e> Gods below, what is there to understand?  I need some nematodes, I
e> had some nematodes, but Hodgkins' wretched peripatus ate them.
Is there anything else that you know about this problem? 
e> Oh forget it.
Could you say more about your problem. 
e> quit.

    In case you were wondering, the e> lines do make sense.

(4) Part of the reason for the frustrating and unhelpful nature of the
    conversation shown is that the program is not Eliza-like.  One of
    the things that Eliza would do is pick up phrases of yours and
    throw them back at you.  For example, after my third input, an
    Eliza-like program might say
	Tell me more about the box.
    			   ^^^^^^^----- copied from input
    or after the last input but two,
	Why did Hodgkins' wretched peripatus eat them?
		^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ transformed input
------------------------------

End of PROLOG Digest
********************