Re: [SWIPL] output multiple query answers to a text file

From: Richard A. O'Keefe <ok_at_cs.otago.ac.nz>
Date: Wed, 9 Jul 2008 14:21:36 +1200

On 9 Jul 2008, at 6:55 am, Ulrich Neumerkel wrote:
> I/O in general is problematic, because it is so side effect ridden.
> But otherwise this is absolute news to me. Since when was it
> considered a mistake in Quintus?

I worked at Quintus from 1985 to 1989 and remained a consultant
for them while they remained in the Prolog business. The
Sun-3/50 they gave me is on the desk next to the one I'm sitting
at. Most of the Quintus library was my work, and after DHDW
left, I maintained the compiler.

So I can answer: adding a stream argument to write/1 and so on
was considered a mistake at Quintus since shortly after I
discovered that the "feature" had been added to a release
without ANY consultation within the company, and for the
reasons I gave. However, since customers were already using
the "feature" by then, it was deemed too late to remove it.

The reason the feature existed was C imitation, rather than
Lisp imitation.

>
>
>> There are several reasons why they are a mistake.
>
>> (1) Swiping the "optional argument" for a stream ...
>
> This would be a good argument if everything could be redone from
> scratch. But we are over that point for 20 years ; compatibility
> rules that out.

Since when was compatibility ever a goal of ISO?
On the contrary, they explicitly rejected that as a criterion
despite my protests. Just because a mistake has been around
for a long time, that doesn't make it any less of a mistake.
And just because a feature _exists_ that doesn't mean you have
to _use_ it.
>
>
>> (2) Code gets seriously cluttered, not merely by passing the stream
>> argument...
>
> I thought stream aliases addressed this problem (also in Quintus) -
> not that I ever used them as they complicate things only further.

No. Stream aliases came from ISO, as I recall. I certainly
cannot find them in the QP documentation. They may perhaps have
been added after I left. I agree that they complicate things
further. However, the main reason they were added to ISO Prolog
was to simplify code migration from other Prolog dialects to
ISO Prolog.

> Otherwise I agree. However, there is still need for open and close
> with all their problems, such as dangling stream-terms (of closed
> streams) that provide access to future unrelated files.

I am having trouble understanding this. Quintus Prolog represented
stream references as '$stream'(N) where N was an integer. But
there was no simple link between the integers N and the stream
objects they referred to: it went through a registry, and the
system kept track of which N were still *reachable* (in the GC
sense) regardless of whether the stream in question was still
open or not. This meant that while "dangling stream-terms"
could certainly exist, they could NOT provide access to future
unrelated files; as long as such a term existed, that value of
N would *never* be used for another file. The only way to
break this was to write a stream-term out to a file and then
read it back later, but I don't know that anyone ever ran into
that. On a 64-bit system, even that would almost certainly not
cause any trouble.

As for open and close, one can do the Lisp thing with
something like with_output_to_file(FileName, Goal); ensure that
it catches exceptions and failures, closes the file, and
reraises the exceptions or fails or whatever.

>
>
>> The _right_ thing to do is something like library(fromonto) from
>> Quintus
>> Goal from_stream Stream
>> Goal onto_stream Stream
>
> I see. To start with, in place of read(Stream, Term) now one writes
> read(Term) from_stream Stream, hopefully simplifying that further;
> maybe even replacing from_stream by from_file. So where is place in
> two arguments for all the options (for open and close in from_file/2 -
> like encodings, kind-of-error-handling for close)?

I didn't say "THE SAME AS", I said "something LIKE".
Let's take from_file/2.

from_file(Goal, FileName) :-
     current_input(OldStream),
     open(FileName, read, NewStream),
     set_input(NewStream),
     ( call(Goal) -> Flag = 1 ; Flag = 0 ),
     set_input(OldStream),
     close(NewStream),
     Flag > 0.

Let's make three changes.
(1) Exception handling.
(2) open options
(3) close options

from_file(Goal, File_Description) :-
     file_description(File_Name, Open_Options, Close_Options),
     current_input(Old_Stream),
     open(File_Name, read, New_Stream, Open_Options),
     set_input(New_Stream),
     catch(( call(Goal) -> Flag = true ; Flag = fail ),
           Any_Exception,
           Flag = throw(Any_Exception)),
     set_output(Old_Stream),
     close(New_Stream, Close_Options),
     call(Flag).

file_description([X|Xs], N, O, [C]) :-
     C = force(_),
     select(C, [X|Xs], Ys),
     select(name(N), Ys, O),
     !.
file_description([X|Xs], N, O, []) :-
     select(name(N), [X|Xs], O),
     !.
file_description(N/O/C, N, O, C) :- !.
file_description(N/O, N, O, []) :- !.
file_description(N, N, [], []).

The file_description/4 predicate is not meant to be taken very
seriously. It is not a considered design, merely a demonstration
that the thing is *possible*. So

     parse_Pascal_program(P) from_file
[name('foobar.pas'),encoding(ascii)]

The real nightmare with things like from_file/2 is what should happen
if the Goal has multiple solutions. (This is analogous to the snags
with re-entering continuations in Scheme.) The solution embodied in
the code above is "don't do that" because the -> will prune them away.

>

> To replay history in what-if mode: One argument against fromonto in
> the context of ISO could have been that such basic operations should
> not incur the cost of a meta-call.

At the time of ISO it was known perfectly well that the overhead of
a meta-call need not be any greater than adding one extra call to
a well-indexed predicate. It was also known that even in systems
with slow meta-calls the cost of a meta-call was far less than the
cost of opening a file, and for that matter, less than the cost of
'assert', which is certainly in ISO.

> Also not completely clear to me are the implications with delayed
> goals. It seems the execution context has now to carry two stream
> arguments for input and output.

But it already does, or have you forgotten current_input/1 and
current_output/1?

> Doesn't that imply that every
> environment has to carry them too?

ISO Prolog does not support delayed goals.

> That would be a hefty overhead.

Why? It's just two pointers. In fact, it only _really_ needs to be
one pointer.

> But to come back to library(fromonto) the central question around
> which all considerations revolve is:
>
> Will this make Prolog stronger or weaker?
>
> Is a further addition encouraging side-effects sans cesse (in the Goal
> argument) just what we need?

It is no such thing. Remember, SWI _already_ has the output
version of this. Having these things in the QP library clearly
*didn't* "encourage side-effects sans cesse"; it merely made it
possible to write the code that people had to write anyway less
error-prone.

Note in particular that 'catch' has to be added to these operations
*once*, and then *all* programs using them start doing something
sensible in the presence of exceptions.

For a long time I was really happy about character I/O in Haskell.
'getContents' returns the contents of standard input as a lazy
list, and writing just does the other thing. However, Haskell
has been steadily moving away from that, and admonitions against
using this 'declaratively pure' approach are frequent in the
Haskell café mailing list, basically because it doesn't really
work in the large scale networked systems people are building in
Haskell these days.

If I want Mercury, I know where to find it. (On my disc...)

--
"I don't want to discuss evidence." -- Richard Dawkins, in an
interview with Rupert Sheldrake.  (Fortean times 232, p55.)
_______________________________________________
SWI-Prolog mailing list
SWI-Prolog_at_iai.uni-bonn.de
https://mailbox.iai.uni-bonn.de/mailman/listinfo.cgi/swi-prolog
Received on Wed Jul 09 2008 - 04:23:17 CEST

This archive was generated by hypermail 2.2.0 : Wed Jul 09 2008 - 04:23:17 CEST