I ask SICStus Prolog ?- at_end_of_stream.
on the top-level and immediately I get: no
.
GNU Prolog and Scryer Prolog do the same.
Traella Prolog and SWI-Prolog, however, choose to wait for input before answering, and rumor has it that behaving like this has been quite common among Prolog systems—especially in the past.
If I look at the ISO-Prolog standard, that behavior becomes spurious:
8.11.8.5 Bootstrapped built-in predicates
The built-in predicatesat_end_of_stream/0
andat_the_of_stream/1
examine the single stream-propertyend_of_stream/1
.A goal
at_end_of_stream
is true iff the current input has a stream position end-of-stream or past-end-of-stream (7.10.2.9, 7.10.2.13).A goal
at_end_of_stream(S_or_a)
is true iff the stream associated with stream or aliasS_or_a
has a stream position end-of-stream.at_end_of_stream :- current_input(S), stream_property(S, end_of_stream(E)), !, (E = at ; E = past).[The the sake of brevity, I omitted the code for
at_end_of_stream/1
.]
So it appears that the standard is quite clearly on the side of SICStus Prolog and GNU Prolog.
So my question boils down to this:
Is this "waiting" behavior simply non-conformance, a kind of anachronism justified on the basis of practicality / compatibility—or is there more to it?
A very simple and basic top-level is like sh
(dash). Prolog top-level is a bit similar, it differs by having auto-completion/choice (like Scryer Prolog, SWI-Prolog), not accepting input with ctrl-D but it could be like sh
.
A query to test and better understand top-level is ?- get_char(C).%a
(where C
binds to %
, source) and ?- get_char(C).
(when submitting with enter, C
binds to \n
, if submitting with ctrl-D then waits or binds C
to end_of_file
).
Why those query?
The top-level can be model as read_term(user_input, Goal, []), call(Goal)
:
get_char(C).%a
submitted with enter: The query is parsed and the user_input
is %a\n
thus C
binds to %
and if get_char(C)
is replaced by at_end_of_stream
then at_end_of_stream/0
fails.get_char(C).
submitted with enter: The query is parsed and the user_input
is \n
thus C
binds to \n
and if get_char(C)
is replaced by at_end_of_stream
then at_end_of_stream/0
fails.get_char(C).
submitted with ctrl-D: The query is parsed and the user_input
is empty. Now the stream position of user_input
should be end-of-stream
or past-end-of-stream
if read_term/3
is executed as procedurally specified in 8.14.1.1
:
user_input
doesn't have an end then with the note of 7.10.2.9
the stream postion can't be end-of-stream
or past-end-of-stream
. Is the stream reset and has end_of_stream
property not
? What happens if the stream doesn't have the property eof_action(reset)
? In any case get_char/1
waits and if get_char(C)
is replaced by at_end_of_stream
then at_end_of_stream/0
fails (there is an opportunity for at_end_of_stream/0
to wait here).user_input
has a size but it's unknown (or variable but still unknown) then:
end-of-stream
then C
binds to end_of_file
and if get_char(C)
is replaced by at_end_of_stream
then at_end_of_stream/0
succeeds.past-end-of-stream
then get_char/1
waits and if get_char(C)
is replaced by at_end_of_stream
then at_end_of_stream/0
succeeds.Sadly there is a bit of twisting at the end.
I might have missed/misread something if not then this undefinedness allows some optimization/simplification of the implementation, explains the difference between engine and also the actual top-level is more complicated that read_term(user_input, Goal, []), call(Goal)
(even if it's the ideal one).
When it comes to at_end_of_stream/0
, it doesn't seem like there is a reason to wait.
If at_end_of_stream/0
is implemented with peek_char/1
without executing eof_action
then that might explain the wait.
On other operating system, it may be different. I use and test on Linux.
On GNU Prolog (version 1.5.0), there is the issue that a stream with property eof_action(reset)
is never at end of stream. This explains why at_end_of_stream/0
fails.
$ echo -n | gprolog --init-goal "(at_end_of_stream, write(at), nl, halt ; peek_char(C), writeq(not(C)), nl, halt)"
not(end_of_file)
$ echo | gprolog --init-goal "(at_end_of_stream, writeq(at), nl, halt ; peek_char(C), writeq(not(C)), nl, halt)"
not('\n')
at_end_of_stream/0
fails even when the stdin is empty.
On Trealla Prolog (version v2.1.11):
$ echo -n | ./tpl -g "(at_end_of_stream, writeq(at), nl, halt ; peek_char(C), writeq(not(C)), nl, halt)"
at
$ echo | ./tpl -g "(at_end_of_stream, writeq(at), nl, halt ; peek_char(C), writeq(not(C)), nl, halt)"
not('\n')
$ echo -n | ./tpl -g "(stream_property(S, alias(user_input)), stream_property(S, end_of_stream(Eos)), at_end_of_stream(S), writeq(eos(Eos)), halt ; halt)"
eos(not)
The property end_of_stream
doesn't agree with at_end_of_stream/1
(permuting stream_property/2
and at_end_of_stream/1
doesn't change the result).
On Scryer Prolog (on master (6b8e6204957bfc3136ea39ec659d30627775260d) or rebis-dev (c1945caf11c0d202f4121de446f1694854dcba47)):
$ echo -n | ./target/release/scryer-prolog -g "(at_end_of_stream, write(at), nl, halt ; peek_char(C), writeq(not(C)), nl, halt)"
not('\x0\')
$ echo | ./target/release/scryer-prolog -g "(at_end_of_stream, write(at), nl, halt ; peek_char(C), writeq(not(C)), nl, halt)"
not('\n')
at_end_of_stream/0
fails even when the stdin is empty.