Subject: Re: [Fwd: abolish/1 in 3.3.0]

From: "Richard A. O'Keefe" <[email-masked]>
Date: Tue, 25 Jan 2000 11:21:35 +1300 (NZDT)

"Richard A. O'Keefe" wrote:
	
	> So it was obvious that the "right" primitive was string streams.
	
Paul Singleton replied:

	Hey Presto.  I think some reasoning has been omitted here, which
	is a pity 'cos I'd want to quibble with it :-)
	
	Streams are not Prolog-like; you cannot backtrack over get/2, and
	you cannot directly parse a stream.  In fact, you cannot directly
	do anything Prolog-like to a stream.
	
Prolog I/O has _never_ been "Prolog-like" since Marseilles Prolog.
Given streams and stream positioning, you _can_ layer backtrackable
I/O on top (at least for file streams).  I have demonstrated this to
people in Prolog classes, showing them for example how you can treat
a(n open) source file as a predicate.  The response was always
"Gosh, that's neat. ...... What use is it?"

I note that Mercury _has_ made I/O fully declarative.  You not only
_can_ parse a stream, you _must_.  I also note that even in Mercury,
you cannot bracktrack over an input or output operation.

The points I listed in my earlier message contained the crucial point:
QP _already_ had streams for file I/O.  Adapting the existing model
was better than _any_ alternative which involved adding an incompatible
model.

	I suggest that the "right" primitive is lazily-evaluated lists of
	characters (whether codes or single-character atoms: we can argue
	about that in another thread :-)
	
I am a happy Haskeller.  Lazy lists are right for Haskell, which is a
lazy functional language.  There is _nothing_ else in Edinburgh Prolog
which resembles lazy lists; adding them just to support string streams
would have meant designing, developing, debugging, documenting &c a
whole new complicated lot of machinery, and that would have been totally
unjustifiable as software engineering unless it were to serve for ALL
I/O, as it does in Haskell.

Something we could have done was to provide the Prolog parser in a form
that could be called by users to parse token lists.  In fact we _did_
do that.  People who wanted to read files into memory as character
lists and then parse them using DCGs were perfectly able to do so.
At 8 bytes per character (cheaper than a lazy list) that makes more
sense on a 64MB machine than on a 4MB machine.

I note that Clean, another and more efficiently implemented lazy
functional language, also has "pure" declarative I/O, but it is closer
to the Mercury model and does not involve passing around lists of any
kind.  I would not like to say that the simple light-weight scheme
used in Mercury and Clean is wrong.

	Jan: (while we're negotiating hooks :-) is it feasible to denote a
	list by a special type of variable which has an associated
	generator, and to invoke this generator whenever an attempted
	unification needs to know more about it (i.e. whether it's empty,
	and if not, what its first element is)?
	
Sounds like coroutining to me.  Have you tried NU Prolog (free academic
licence, IIRC) or SICStus Prolog?

	I'm guessing that a multi-threaded kernel is necessary (in which
	case string streams were the best "wrong" primitive for Quintus at
	the time :-)
	
Oddly enough, Quintus actually built a multi-threaded system (Tim Lindholm	
did the work) with the intention of exploiting Sequents and other
multi-CPU systems.  As far as I know, it never shipped, which was a pity.

The reasons why string streams were the best _engineering_ choice at the
time I have already explained.