# \`term\_string/2\` returns \`end\_of\_file\` with null string at the second arg

**URL:** https://swi-prolog.discourse.group/t/term-string-2-returns-end-of-file-with-null-string-at-the-second-arg/7403
**Category:** General
**Created:** [April 26, 2024, 7:52am UTC](https://swi-prolog.discourse.group/t/term-string-2-returns-end-of-file-with-null-string-at-the-second-arg/7403 "2024-04-26T07:52:37Z")
**Posts on this page:** 11
**Page:** 2

<div class="post-metadata">

### Author: ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)
#### Post date: [April 29, 2024, 6:29pm UTC](https://swi-prolog.discourse.group/t/term-string-2-returns-end-of-file-with-null-string-at-the-second-arg/7403/21 "2024-04-29T18:29:54Z")

</div>

We can clear end-of-file on streams. That is used for the console where ^D (end-of-file) terminates a consult of `?- [user].` The system resets the end-of-file marker such that we can continue. But, term\_string/2 is a conversion for exactly one term.

---

<div class="post-metadata">

### Author: ![peter.ludemann](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/peter.ludemann/32/48_2.png) [@peter.ludemann](https://swi-prolog.discourse.group/u/peter.ludemann)
#### Post date: [April 29, 2024, 7:28pm UTC](https://swi-prolog.discourse.group/t/term-string-2-returns-end-of-file-with-null-string-at-the-second-arg/7403/22 "2024-04-29T19:28:21Z")

</div>

> [@ridgeworks](#):
>
> Prolog is a logic programming language. Failures should always be expected; they’re built into the fundamental paradigm of the language (while errors are non-logical). Turning unexpected failures (and errors) into expected failures is part of the debugging process, for which a pretty capable debugger is provided.

I strongly prefer pure logical code – in fact, that was the original aim of my thesis years ago (which got sidetracked quite a bit).

But bitter experience has taught me that pure logical code is difficult to debug, and the current state of art for debuggers isn’t at all “capable” for finding these bugs. I would argue that the solution isn’t a better debugger but better declarations, such as det/1 or SSU, plus type inferencing on steroids.

(Constraints don’t play well with cuts; and experience has taught me that they’re even more difficult to get right.)

So, in the absence of other tools, I prefer errors to failure when it’s not obvious which is “best”. Before the det/1 directive, I wrote my own “must\_succeed” support because tracking down unexpected failures was too difficult (my code tended to use enormous data structures, which weren’t very suitable for the debugger). A related problem was predicates that left unexpected backtrack points, for which det/1 also helped. But, as I’ve said, det/1 and SSU aren’t enough; which is why I prefer throwing an error when there’s an error (as opposed to an inconsistency, for which failure is the best approach).

---

<div class="post-metadata">

### Author: ![ridgeworks](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/ridgeworks/32/886_2.png) [@ridgeworks](https://swi-prolog.discourse.group/u/ridgeworks)
#### Post date: [April 30, 2024, 3:01pm UTC](https://swi-prolog.discourse.group/t/term-string-2-returns-end-of-file-with-null-string-at-the-second-arg/7403/23 "2024-04-30T15:01:10Z")

</div>

Different strokes for different folks. I do distinguish between between low level code (built-in’s) and, to a lesser extent library API’s, and application code where it’s a programmer’s choice, and maybe even a requirement, that errors be generated.

For this particular case, if I had a vote, I would prefer fail - logically speaking, there is no term which corresponds to the empty string. The status quo (`end_of_file`) is the least attractive option, because it’s ambiguous:

```prolog
?- term_string(T,""), term_string(T,"end_of_file").
T = end_of_file.

```

but I believe this has been fixed in 9.3.5. (Also `read_term_from_atom/3`, `atom_to_term/3` ?)

However, I don’t anticipate any changes in current API’s. I guess I would like an efficient way of implementing:

```prolog
silent(G) :- catch(G,_,fail).

```

So two things:

1. somehow avoid meta-call (use goal expansion?)
2. avoid building and throwing exception if handler = `fail`

> [@peter.ludemann](#):
>
> (Constraints don’t play well with cuts; and experience has taught me that they’re even more difficult to get right.)

Off-topic, but I’ve never really understood this. Do you mean constraints as in `library(clpfd)` and `dif/2` or constraints as in arbitrary user defined code that has its execution conditionally deferred?

In some ways deferred execution is like concurrency - how does one manage errors in, e.g., `concurrent_maplist`?

---

<div class="post-metadata">

### Author: ![kuniaki.mukai](https://avatars.discourse-cdn.com/v4/letter/k/c67d28/32.png) [@kuniaki.mukai](https://swi-prolog.discourse.group/u/kuniaki.mukai)
#### Post date: [April 30, 2024, 4:05pm UTC](https://swi-prolog.discourse.group/t/term-string-2-returns-end-of-file-with-null-string-at-the-second-arg/7403/24 "2024-04-30T16:05:34Z")

</div>

> [@Ann: SWI-Prolog 9.3.5](https://swi-prolog.discourse.group/t/ann-swi-prolog-9-3-5/7425/1):
>
> - MODIFIED: [term\_string/2](https://www.swi-prolog.org/pldoc/doc_for?object=term_string/2),3, atom\_to\_term/2 and [read\_term\_from\_atom/3](https://www.swi-prolog.org/pldoc/doc_for?object=read_term_from_atom/3)  
> now raise exception on empty input These predicates used to  
> produce the atom `end_of_file`. Now they raise the exception  
> syntax\_error(end\_of\_string).

Thanks. I have checked that prolog cgi pages of mine now works as expected with respect to the new “end\_of\_file” syntax error for `term_string`.

However, I notice that we still see `end_of_file` or `-1` for queries like below. As there is prepared ‘at\_end\_of\_stream/0,1’ IMHO, it seems better to hide such “end\_of\_file” marks from the user. I know -1 has been used since Edinburgh Prolog. So I understand such explicit but artificial end\_of\_file terms are mainly from historical reasons, I guess, but I may be wrong.

```prolog
?- open_string("", S), get_code(S, C), close(S).
S = <stream>(0x600001ee8000),
C = -1.

?- open_string("", S), at_end_of_stream(S), close(S).
S = <stream>(0x600001e72000).

?- open_string("", S), at_end_of_stream(S), read(S, X), close(S).
S = <stream>(0x600001ef7100),
X = end_of_file.

```

---

<div class="post-metadata">

### Author: ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)
#### Post date: [May 1, 2024, 7:30am UTC](https://swi-prolog.discourse.group/t/term-string-2-returns-end-of-file-with-null-string-at-the-second-arg/7403/25 "2024-05-01T07:30:40Z")

</div>

> [@kuniaki.mukai](#):
>
> However, I notice that we still see `end_of_file` or `-1` for queries like below

Both are part of tradition as well as ISO standard. There is nothing wrong with -1 as it is not a valid character and thus an unambiguous indication for end-of-file. The problem with read/2 is there there is no “out-of-band” term. We do need it. Consider

```
hello. /* this is the end */

```

read/2 returns `hello`, leaving the file position after the full-stop (. + white). So, at\_end\_of\_stream/1 still fails. Calling read again must return something. This is `end_of_file`. So, if your input is guaranteed to not contain `end_of_file`, you can test for that. If you want unambiguous handling of terms you need to check for read/2 to return `end_of_file` **and** test at\_end\_of\_stream/1 to succeed.

Of course we could also throw an exception on end-of-file, but this is IMO wrong. First of all, in general we would like to avoid the need for exception handling to to perfectly normal things and second, you want to be able to handle exceptions at a higher level. In this use case you’d typically have to handle it completely local.

> [@ridgeworks](#):
>
> So two things:
> 
> 1. somehow avoid meta-call (use goal expansion?)
> 2. avoid building and throwing exception if handler = `fail`

Requiring local handling is the situation @ridgeworks is in: he would like to handle all exceptions as local as possible as failures. That is indeed not very well supported in the current system. If it needs to be resolved I’d go for a more efficient catch/3 implementation. That is surely possible, but as far as I’m concerned not very high on the priority list. Even for @ridgeworks use cases, spending the same time on general optimization is likely to have more impact.

---

<div class="post-metadata">

### Author: ![ridgeworks](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/ridgeworks/32/886_2.png) [@ridgeworks](https://swi-prolog.discourse.group/u/ridgeworks)
#### Post date: [May 1, 2024, 3:14pm UTC](https://swi-prolog.discourse.group/t/term-string-2-returns-end-of-file-with-null-string-at-the-second-arg/7403/26 "2024-05-01T15:14:25Z")

</div>

> [@jan](#):
>
> Requiring local handling is the situation @ridgeworks is in: he would like to handle all exceptions as local as possible as failures. That is indeed not very well supported in the current system. If it needs to be resolved I’d go for a more efficient [catch/3](https://www.swi-prolog.org/pldoc/doc_for?object=catch/3) implementation. That is surely possible, but as far as I’m concerned not very high on the priority list. Even for @ridgeworks use cases, spending the same time on general optimization is likely to have more impact.

Totally agree. I rarely use catch/3 and instead depend on causing “premature failure” using a guard (usually type filters which are VM instructions) before calling a predicate which may generate an error. So this is more of a philosophical niggle (small code bloat) than a practical concern.

On the other other hand, general optimizations are always welcome.

---

<div class="post-metadata">

### Author: ![kuniaki.mukai](https://avatars.discourse-cdn.com/v4/letter/k/c67d28/32.png) [@kuniaki.mukai](https://swi-prolog.discourse.group/u/kuniaki.mukai)
#### Post date: [May 1, 2024, 4:09pm UTC](https://swi-prolog.discourse.group/t/term-string-2-returns-end-of-file-with-null-string-at-the-second-arg/7403/27 "2024-05-01T16:09:33Z")

</div>

> [@jan](#):
>
> There is nothing wrong with -1 as it is not a valid character and thus an unambiguous indication for end-of-file.

I agree with this on -1.

> [@jan](#):
>
> The problem with [read/2](https://www.swi-prolog.org/pldoc/doc_for?object=read/2) is there there is no “out-of-band” term. We do need it. Consider

Going to test your comment on end\_of\_file, I get confused to see the result  
of preparatory queries, which seems to mean that the `end_of_file` atom  
is not necessary for `read`, provided that `at_end_of_stream` because it returns ‘syntax error’ correctly for such error item on the input stream. I’m afraid I might be missing points.

```prolog
% ?- open_string("end_of_file. end_of_file. ", S), read(S, X), read(S, Y), read(S, Z).
%@ S = <stream>(0x600001de4400),
%@ X = Y, Y = Z, Z = end_of_file.

% ?- open_string("a. b. ", S), read(S, X), read(S, Y), read(S, Z).
%@ S = <stream>(0x600001de2800),
%@ X = a,
%@ Y = b,
%@ Z = end_of_file.

% ?- open_string("a.\nb.\n", S), read(S, X), read(S, Y), read(S, Z).
%@ S = <stream>(0x600001ddae00),
%@ X = a,
%@ Y = b,
%@ Z = end_of_file.

% ?- open_string("a. b. /* / */", S), read(S, X), read(S, Y), read(S, Z).
%@ S = <stream>(0x600001ddaf00),
%@ X = a,
%@ Y = b,
%@ Z = end_of_file.

% ?- open_string("a. b. / / */ ", S), read(S, X), read(S, Y), read(S, Z).
%@ ERROR: Stream <stream>(0x600001ddb000):1:14 Syntax error: Unexpected end of file
%@ ^ Exception: (4) setup_call_cleanup('$toplevel':notrace(call_repl_loop_hook(begin, 0)), '$toplevel':'$query_loop'(0), '$toplevel':notrace(call_repl_loop_hook(end, 0))) ? creep

```

---

<div class="post-metadata">

### Author: ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)
#### Post date: [May 1, 2024, 4:37pm UTC](https://swi-prolog.discourse.group/t/term-string-2-returns-end-of-file-with-null-string-at-the-second-arg/7403/28 "2024-05-01T16:37:49Z")

</div>

> [@kuniaki.mukai](#):
>
> Going to test your comment on end\_of\_file, I get confused to see the result  
> of preparatory queries, which seems to mean that the `end_of_file` atom  
> is not necessary for `read`, provided that `at_end_of_stream` because it returns ‘syntax error’ correctly for such error item on the input stream. I’m afraid I might be missing points.

I don’t understand this. All results are to be expected. The problem is that if we do not return `end_of_file` on reaching the end of file, but rely on at\_end\_of\_stream/1, We cannot detect that `b` is the last term in this example by you

> [@kuniaki.mukai](#):
>
> `?- open_string("a. b. /* / */", S)`

After reading b, at\_end\_of\_stream/1 is still false. So, we must call read/2 one more time. What should that call do? Sure, it will read up to end-of-file, such that after this call at\_end\_of\_stream/1 is true. We can either succeed, but than we need to bind the output term to _something_ (or not, but a variable is also a valid Prolog term, so that does not help). Or we need to fail, but that also leads to pretty awkward code or raise an exception, which is even worse. So, we have little choice but bind the term to something. Traditionally this is `end_of_file`.

---

<div class="post-metadata">

### Author: ![kuniaki.mukai](https://avatars.discourse-cdn.com/v4/letter/k/c67d28/32.png) [@kuniaki.mukai](https://swi-prolog.discourse.group/u/kuniaki.mukai)
#### Post date: [May 1, 2024, 5:02pm UTC](https://swi-prolog.discourse.group/t/term-string-2-returns-end-of-file-with-null-string-at-the-second-arg/7403/29 "2024-05-01T17:02:47Z")

</div>

Thanks I got point.  
I thought it may not be difficult to add to at\_end\_of\_stream an action to skip fillers and comments until next start position of possible prolog term or to the true “end\_of\_file.”

---

<div class="post-metadata">

### Author: ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)
#### Post date: [May 2, 2024, 7:14am UTC](https://swi-prolog.discourse.group/t/term-string-2-returns-end-of-file-with-null-string-at-the-second-arg/7403/30 "2024-05-02T07:14:53Z")

</div>

> [@kuniaki.mukai](#):
>
> I thought it may not be difficult to add to at\_end\_of\_stream an action to skip fillers and comments until next start position of possible prolog term or to the true “end\_of\_file.”

at\_end\_of\_stream/1 is not aware of the content of the stream. You would need multiple of these predicates depending on the content. That sounds hopelessly complicated. I think it is as good as it can be. Another line of thought I once had is to allow defining “out-of-bound” values. Something like that already exists for dicts, that are compounds with a functor that has no Prolog syntax. Similarly, we could define an out-of-bound end-of-file constant and a predicate to test for this. Given that, we could do

```
read(In, Term),
( is_end_of_file(Term)
-> done
; ...
).

```

I proposed things along these lines with the version 7 extensions, but there was no warm welcome …

---

<div class="post-metadata">

### Author: ![kuniaki.mukai](https://avatars.discourse-cdn.com/v4/letter/k/c67d28/32.png) [@kuniaki.mukai](https://swi-prolog.discourse.group/u/kuniaki.mukai)
#### Post date: [May 2, 2024, 8:05am UTC](https://swi-prolog.discourse.group/t/term-string-2-returns-end-of-file-with-null-string-at-the-second-arg/7403/31 "2024-05-02T08:05:54Z")

</div>

> [@jan](#):
>
> [at\_end\_of\_stream/1](https://www.swi-prolog.org/pldoc/doc_for?object=at_end_of_stream/1) is not aware of the content of the stream. You would need multiple of these predicates depending on the content. That sounds hopelessly complicated.

Sorry, I could not imagine such implementation difficulty. You are always busy on  
more important issues. I do not insist, and I should be silent on this.

BTW, the following `is_end_of_file` seems to be almost equivalent what I had in mind  
as functionality to skipping filler contents on the stream, which is expected to free the ‘end\_of\_file’ atom from the reserved but not elegant mission. I hope it is still a conservative elegant extension on the current stream I/O.

> [@jan](#):
>
> ```prolog
> read(In, Term),
> ( is_end_of_file(Term)
> -> done
> ; ...
> ).
> 
> ```

[Previous page](https://swi-prolog.discourse.group/t/term-string-2-returns-end-of-file-with-null-string-at-the-second-arg/7403.md?page=1)
