[comp.lang.prolog] different Prologs?

ddgg0881@uxa.cso.uiuc.edu (10/09/89)

I have two questions about Prolog implementations:

1. I've discovered one implementation that gives 3 solutions to
member(a,[a,a,a]) even though there aren't 3 ways to instantiate
variables. Another implementation I've used gives only one solution.
Which way is standard? Are there arguments for doing things one way
or the other?

2. I find reading text files very painful in Prolog because you
always have to keep a lookahead character. This way of doing things
doesn't feel like Prolog. One of the main selling points of Prolog
is the efficient way that it backtracks upon failure. Are there any
implementations of Prolog that allow 'get' or 'get0' to backtrack
and unread characters?

Dale Gerdemann
University of Illinois, Dept of Linguistics
Cognitive Science Group, Beckman Institute
dale@tarski.cogsci.uiuc.edu
d-gerdemann@uiuc.edu

ok@cs.mu.oz.au (Richard O'Keefe) (10/10/89)

In article <111000002@uxa.cso.uiuc.edu>, ddgg0881@uxa.cso.uiuc.edu writes:
> I have two questions about Prolog implementations:

> 1. I've discovered one implementation that gives 3 solutions to
> member(a,[a,a,a]) even though there aren't 3 ways to instantiate
> variables.  Another implementation I've used gives only one solution.
> Which way is standard? Are there arguments for doing things one way
> or the other?

There is precisely one "standard" way of defining member/2,
modulo spelling of variable names and dot-vs-bracket syntax for lists:

	member(X, [X|_]).
	member(X, [_|L]) :-
		member(X, L).

Prolog does *not* find solutions.  Prolog finds **proofs**, and shows you
(part of) the substitution it found for each proof.  The goal
	?- member(a, [a,a,a]).
has three proofs.  So that's the number of times Prolog should come back
to you with an answer substitution.  However, there is a fine point to do
with the user interface.  Many Prolog systems check the query that you
type in, and if it hasn't got any variables, just say "yes" or "no".
NU Prolog does this, except it says "true" or "false".  Transcript:
	% np
	NU-Prolog 1.4
	1?- member(a, [a,a,a]).
	true.
However, if you put a variable in there to fool it, you will see that it
is prepared to find the other proofs.  Transcript:
	2?- X=dummy, member(a, [a,a,a]).
	X = dummy ;
	X = dummy ;
	X = dummy ;
	fail.

Another way of seeing that the three proofs are found is
	3?- member(a, [a,a,a]), write(*), fail.
	***fail.

> 2. I find reading text files very painful in Prolog because you
> always have to keep a lookahead character.

So?  You need a lookahead character for tokenising anyway, all you have
to do is remember not to drop it on the floor.  Just write down your DFSA
and the transcription into Prolog is direct, but DIRECT.

> This way of doing things doesn't feel like Prolog.

Of course not, because it uses side effects.  Depending on the size of
the files you want to process, and the memory capacity of your machine,
you might want to consider slurping the entire file into memory as a
list of character codes, and parsing that using grammar rules.  I'm
quite serious about this:

	get_file(FileName, Chars) :-
		seeing(CurrentInput),
		see(FileName),
		get0(C),
		slurp(C, Cs),
		seen,
		see(CurrentInput),
		Chars = Cs.

	slurp(-1, []) :- !.
	slurp(C, [C|Cs]) :-
		get0(D),
		slurp(D, Cs).

In SICStus Prolog 0.6 it takes about 6 seconds to read 30kbytes this
way on a discless Sun-3/50.

> One of the main selling points of Prolog
> is the efficient way that it backtracks upon failure.  Are there any
> implementations of Prolog that allow 'get' or 'get0' to backtrack
> and unread characters?

Yes.  Sort of.  If you are reading from a file there is no problem.  But
what if you are reading from a source that can't back up?  (For example,
reading from a terminal --very likely-- or a UNIX command --I do that
quite often-- or a named pipe --quite possible-- or a streaming tape
--just joking.)  Then the Prolog system would have to buffer an
arbitrary amount of information just in case you decided to back up.
Your Prolog system probably provides some kind of file positioning
feature.  Just for grins, here's how to hook it into grammar rules.

	file(NonTerminal, File) :-
		open(File, read, Stream),
		stream_position(Stream, Position),
		phrase(NonTerminal, @(Stream,Position), @(Stream,End)),
		stream_position(Stream, _, End),	% high-water mark
		get0(-1).				% is end-of-file

	get_char(Char, @(Stream,OldPos), @(Stream,NewPos)) :-
		stream_position(Stream, _, OldPos),	% go to OldPos
		get0(Char),				% pick up Char
		Char >= 0,				% check not EOF
		stream_position(Stream, NewPos).	% where are we now?

Now your grammar rules, instead of saying
		[Char]
will say	get_char(Char)
as a non-terminal.  In NU Prolog, which provides the non-portable
ftell/2 and fseek/3 constructs (they are fine in UNIX, but not so good in
VMS or MVS or CMS), it's even simpler:

	stream_phrase(NonTerminal, Stream) :-
		open(File, read, Stream),		
		ftell(Stream, Start),
		fseek(Stream, 0, end),
		ftell(Stream, Finish),
		call(NonTerminal, @(Stream,Start), @(Stream,Finish)).

	get_char(Char, @(Stream,OldPos), @(Stream,NewPos)) :-
		plus(1, OldPos, NewPos),
		fseek(Stream, OldPos, beginning),
		get0(Char),
		Char >= 0.

	call(Closure, X1, X2) :-
		Closure =.. L0,
		append(L0, [X1,X2], L2),
		Goal =.. L2,
		call(Goal).

ok@cs.mu.oz.au (Richard O'Keefe) (10/10/89)

In article <2365@munnari.oz.au>, ok@cs.mu.oz.au I wrote
> 	file(NonTerminal, File) :-

which should have been
	file_phrase(NonTerminal, File)

> 	stream_phrase(NonTerminal, Stream) :-

which should also have been
	file_phrase(NonTerminal, File)

gupta@surya.cs.unc.edu (Gopal Gupta) (10/10/89)

In article <111000002@uxa.cso.uiuc.edu>, ddgg0881@uxa.cso.uiuc.edu writes:
> 
> 
> I have two questions about Prolog implementations:
>
> 1. ........
> 
> 2. I find reading text files very painful in Prolog because you
> always have to keep a lookahead character. This way of doing things
> doesn't feel like Prolog. One of the main selling points of Prolog
> is the efficient way that it backtracks upon failure. Are there any
> implementations of Prolog that allow 'get' or 'get0' to backtrack
> and unread characters?

We implemented a logic language with functional syntax called EqL
that allows both backtrackable as well as non-backtrackable 
reads and writes. Thus, if computation fails over a backtackable read, 
the file pointer is set back accordingly (however, this is actually 
not how this was implemented). In case of backtrackable writes, for
obvious reasons the data can be output to a file only after the 
query execution is over. 

The EqL interpreter (for a Sun or a Vax) and manual can be obtained by 
anonymous ftp from dopey.cs.unc.edu (Internet Addr. 128.109.136.82),
if someone is interested. EqL is also compilable to DEC 3100s and
Apollos.

I vaguely remember some Prolog implementation also having backtrackable 
reads and writes but I can't recall the name right now. More knowledgable
people in the net would perhaps know better.

Regards.

------------------------------------------------
--Gopal Gupta,				   gupta@unc.cs.unc.edu
  Department of Computer Science,	   ..!decvax!mcnc!unc!gupta
  CB# 3175 Sitterson Hall, 
  University of North Carolina ,
  Chapel Hill, N.C. 27599-3175.
  U.S.A.

miller@CS.ROCHESTER.EDU (Brad Miller) (10/11/89)

    Prolog does *not* find solutions.  Prolog finds **proofs**, and shows you
    (part of) the substitution it found for each proof.  The goal
	    ?- member(a, [a,a,a]).
    has three proofs.  So that's the number of times Prolog should come back
    to you with an answer substitution.  However, there is a fine point to do
    with the user interface.  Many Prolog systems check the query that you
    type in, and if it hasn't got any variables, just say "yes" or "no".
    NU Prolog does this, except it says "true" or "false".  Transcript:
	    % np
	    NU-Prolog 1.4
	    1?- member(a, [a,a,a]).
	    true.
    However, if you put a variable in there to fool it, you will see that it
    is prepared to find the other proofs.  Transcript:
	    2?- X=dummy, member(a, [a,a,a]).
	    X = dummy ;
	    X = dummy ;
	    X = dummy ;
	    fail.

    Another way of seeing that the three proofs are found is
	    3?- member(a, [a,a,a]), write(*), fail.
	    ***fail.

On the other hand, it seems perfectly reasonable for a PROLOG or any logic
language implementation to note that in the variable case above, reproving
the member doesn't change the binding of X and refuse to do it, rather
simply fail. This is what would happen if "intelligent backtracking" were
being used. Similarly, in the last example, since no variable can be rebound
by the current frame, fail should exit the proof immediately after the first
write.

I think part of the issue is that Prolog really doesn't find "proofs" in the
logical sense, rather it does SLD resolution. That is to say, one shouldn't
confuse PROLOG, which is a programming language, with LOGIC which isn't.
That is, it does not return proof trees. If it did, then techniques like
intelligent backtracking could not be used, nor could "side effects" be
allowed (Side effects in the programming language sense).

miller@CS.ROCHESTER.EDU (Brad Miller) (10/11/89)

[[Protect quoted material from broken mail interface to pnews]]

    Date: 10 Oct 89 02:02:18 GMT
    From: ok@cs.mu.oz.au (Richard O'Keefe)

    Prolog does *not* find solutions.  Prolog finds **proofs**, and shows you
    (part of) the substitution it found for each proof.  The goal
	    ?- member(a, [a,a,a]).
    has three proofs.  So that's the number of times Prolog should come back
    to you with an answer substitution.  However, there is a fine point to do
    with the user interface.  Many Prolog systems check the query that you
    type in, and if it hasn't got any variables, just say "yes" or "no".
    NU Prolog does this, except it says "true" or "false".  Transcript:
	    % np
	    NU-Prolog 1.4
	    1?- member(a, [a,a,a]).
	    true.
    However, if you put a variable in there to fool it, you will see that it
    is prepared to find the other proofs.  Transcript:
	    2?- X=dummy, member(a, [a,a,a]).
	    X = dummy ;
	    X = dummy ;
	    X = dummy ;
	    fail.

    Another way of seeing that the three proofs are found is
	    3?- member(a, [a,a,a]), write(*), fail.
	    ***fail.

On the other hand, it seems perfectly reasonable for a PROLOG or any logic
language implementation to note that in the variable case above, reproving
the member doesn't change the binding of X and refuse to do it, rather
simply fail. This is what would happen if "intelligent backtracking" were
being used. Similarly, in the last example, since no variable can be rebound
by the current frame, fail should exit the proof immediately after the first
write.

I think part of the issue is that Prolog really doesn't find "proofs" in the
logical sense, rather it does SLD resolution. That is to say, one shouldn't
confuse PROLOG, which is a programming language, with LOGIC which isn't.
That is, it does not return proof trees. If it did, then techniques like
intelligent backtracking could not be used, nor could "side effects" be
allowed (Side effects in the programming language sense).

ok@cs.mu.oz.au (Richard O'Keefe) (10/11/89)

In article <1989Oct10.170254.9642@cs.rochester.edu:, miller@CS.ROCHESTER.EDU (Brad Miller) writes:
(I wrote)
:     Prolog does *not* find solutions.  Prolog finds **proofs**, and shows you
:     (part of) the substitution it found for each proof.  The goal
: 	    ?- member(a, [a,a,a]).
:     has three proofs.

: On the other hand, it seems perfectly reasonable for a Prolog or any logic
: language implementation to note that in the variable case above, reproving
: the member doesn't change the binding of X and refuse to do it, rather
: simply fail. This is what would happen if "intelligent backtracking" were
: being used. Similarly, in the last example, since no variable can be rebound
: by the current frame, fail should exit the proof immediately after the first
: write.

Reasonable?  I'd be happy for NU Prolog to do something like that because
NU Prolog has the ":- pure" declaration.  In the absence of such a declaration,
it is NOT reasonable for a Prolog system to do that.  (Think: repeat/0 has 
ONE solution but infinitely many proofs; it's used precisely because of the
latter property.)

There's another sense of "reasonable"; intelligent backtracking ranges from
outrageously costly to unpleasantly costly.  There are affordable versions
of intelligent backtracking around, but they become affordable by sacrificing
much of the intelligence.  You can avoid repeated solutions to a goal _this_
simple, but the problem is going to come right back in more complicated
situations.  Another method is to use extension tables (applicable only to
:- pure predicates), which have their own problems.

: I think part of the issue is that Prolog really doesn't find "proofs" in the
: logical sense, rather it does SLD resolution. That is to say, one shouldn't
: confuse Prolog, which is a programming language, with LOGIC which isn't.
: That is, it does not return proof trees. If it did, then techniques like
: intelligent backtracking could not be used, nor could "side effects" be
: allowed (Side effects in the programming language sense).

Miller confuses FINDING proofs with REPORTING them.  Just because Prolog
"does not return proof trees" doesn't mean that it "doesn't find proofs".
As a matter of fact some debugging tools _do_ return proof trees, which
doesn't make them any the less Prolog.  Predicate's such as findall/3
and VM/Prolog's call/2 (at least I think that's what they call it) let
you discover how many proofs were found for a goal; intelligent backtracking
would yield different answers.

As they say in the satellite business, there's no such thing as a free
launch.  Prolog strikes a balance between cost and power; it's fast and
stupid rather than slow and smart.

ferguson@cs.rochester.edu (George Ferguson) (10/11/89)

In article <111000002@uxa.cso.uiuc.edu>, ddgg0881@uxa.cso.uiuc.edu writes:
> I have two questions about Prolog implementations:
> One of the main selling points of Prolog
> is the efficient way that it backtracks upon failure.  Are there any
> implementations of Prolog that allow 'get' or 'get0' to backtrack
> and unread characters?

WUP (Waterloo Unix Prolog) claimed to implement both _logical_ (ie. back-
trackable) I/O predicates based (I think) on streams as well as the
standard non-logical I/O predicates.

The best reference I can find is the User's manual which reads:

	WUP Version 3.0 User's Manual
	David J. McClurkin
	Logic Programming and AI Group
	Department of Computer Science
	University of Waterloo
	Waterloo, Ontario, Canada
	[ version of August 6, 1987 ]

Perhaps you can track it down if you're interested, I never used the
system myself.

-- 
George Ferguson			ARPA: ferguson@cs.rochester.edu
University of Rochester		UUCP: {decvax,rutgers}!rochester!ferguson
Rochester  NY  14627		VOX:  (716) 275-2527

lee@munnari.oz.au (Lee Naish) (10/12/89)

In article <111000002@uxa.cso.uiuc.edu>, ddgg0881@uxa.cso.uiuc.edu writes:
> implementations of Prolog that allow 'get' or 'get0' to backtrack
> and unread characters?

From the bts list of Prologs:

NAME:           Basser Prolog
VERSION:        3
SRC/MACHINE/OS: C/Unix (V7, 4.?BSD & others) also VMS
AVAILABILITY:   Educational license, source or binary
COST:           A$300 or US$300 or negotiable, VMS version extra
STATUS:         running, some development & support
FEATURES:
    Fast interpreter, full interface to Unix, Dec-10 debugging
    facilities, real arithmetic, backtrackable I/O, good docu-
				 ^^^^^^^^^^^^^^^^^
    mentation (user manual & tutorial).
CONTACT:        Andrew Taylor (USENET: mulga!basser!andrewt)
                Department of Computer Science
                Sydney University
                Sydney, N.S.W.  2006
                Australia
NOTES:
    Yet another prolog syntax. Most of the code is clean and readable.
    Generally similar to DEC-10 prolog.  Basser Prolog has sufficient
    "features" to make it a reasonable programming environment and a
    useful tool.
DATED:          May 1984

As far as I know Andrew is still at S.U.  The address is a bit out of
date - try andrewt@cs.su.oz.au

	lee

goldfain@osiris.cso.uiuc.edu (10/17/89)

] /* Oct 11, 1989   ok@cs.mu.oz.au writes in comp.lang.prolog */
] ...
] There's another sense of "reasonable"; intelligent backtracking ranges from
] outrageously costly to unpleasantly costly.  There are affordable versions
] of intelligent backtracking around, but they become affordable by sacrificing
] much of the intelligence.  You can avoid repeated solutions to a goal _this_
] simple, but the problem is going to come right back in more complicated
] situations.  Another method is to use extension tables (applicable only to
] :- pure predicates), which have their own problems.
] ...
] As they say in the satellite business, there's no such thing as a free
] launch.  Prolog strikes a balance between cost and power; it's fast and
] stupid rather than slow and smart.
/* End of text from comp.lang.prolog */

Hmmm... This is one other place where one could explore parallelism: have
separate processors hunting for the best or simplest backtrack scheme.
This sounds vaguely like it might be a psychologically plausible model.  I
don't know what kind of a nightmare this would be to implement.

I think the general approach to parallelism in Prolog is somewhat incompatible
with this idea - all promising approaches are being explored, so an
intelligent backtrack would never be involved.  But I see no proof that the
idea doesn't have unique benefits in some problem areas, so I can't dismiss it
as a pointless investigation.
                                                  - Mark Goldfain

miller@CS.ROCHESTER.EDU (Brad Miller) (10/18/89)

[]

    Date: 17 Oct 89 09:40:15 GMT
    From: goldfain@osiris.cso.uiuc.edu

    I think the general approach to parallelism in Prolog is somewhat incompatible
    with this idea - all promising approaches are being explored, so an
    intelligent backtrack would never be involved.  

Consider the case where a proof deep in the tree (admitably in parallel)
identifies a culprit high up in the tree. You want to prune the tree and
kill off all the processes currently exploring proofs in that part of the
tree, freeing them up as a resource for exploring other branches.

Proof cacheing probably buys you more in most cases than intelligent
backtracking, but that's another case where [member ?x (:A :A :A)] would
only succeed once. (Sorry for the Rhet syntax :-); I'm sure the translation
is obvious).