`term_string/2` returns `end_of_file` with null string at the second arg

I forgot reason why end_of_file is returned by ‘term_string’ for the null string.
Is this widely accepted in Prolog community ? Certainly, it seems difficult
to define a term for the string “” other than the end_of_file.

?- term_string("", R).
R = "\"\"".
% ?- term_string(A, '').
%@ A = end_of_file.
% ?- term_string(A, "").
%@ A = end_of_file.
% ?- term_string(A, 'a').
%@ A = a.

The return of end_of_file is the consequence of the parser being started on the string. As no term serializes to an empty string, we could consider raising a domain error instead. Note that this also applies for strings that only contain white space and/or comments.

Any opinions on yes/no raising a domain_error? (or some other error).

I happened to clear a text area of a browser carelessly, then prolog cgi of mine took it
as if the text ‘end_of_file’ is there. I took some time to find what happened. So, as far as this case of mine, raising domain error might have been useful to make debug time shorter to find the input error.

Another option might be to throw an unexpected end-of-file syntax_error exception. Any objections, i.e., a reason to keep returning end_of_file?

I already adopted this into some related predicate which calls term_string/2
possibly with the empty string, as a minimal workaround for not going into serious debugging.

Why not just simple failure of term_string(T, "") because there is no valid term. In the same way that e.g. between(4, 3, N) returns failure rather than an exception “3 is not in the domain of 4 or above” :grinning:

Exceptions seem more awkward. Failure seems perfectly logical, no need for more.

An error can give more information, such as where the syntax error was detected. And an error can be easily converted to failure, using catch/3.

Yes, but even better is to not have to jump through those hoops in the first place, when they don’t add significant benefit :wink:

That could just be the same predicate with a different arity, to add a variable indicating syntax error and where. To be used when the program cares about such details.

I suppose my point boils down to: shouldn’t exceptions be the last choice rather than the first choice, in Prolog?

I suppose it boils down to whether you think a predicate is something that can either succeed (possibly multiple times) or fail; or is it a predicate that should always succeed (there are a few other less common permutations that I’m ignoring). If the latter, I think that throwing an exception makes sense but if the former, then an exception doesn’t make sense.

If I’m programming in C or Go, I need to check the return code after every call, which clutters the code. If I’m programming in Python, Java, or C++, I can use a try/catch if I care about an error (I probably do care for production code, which is why Go’s style is fine for production code) but otherwise I can assume that execution will stop if something unexpected happens. Things like opening a file (which can fail if the file doesn’t exist) or parsing a Prolog term are somewhere at the boundary between the two situations … for example, it might make sense to have an open/5 that unifies the 5th argument with ok or error(...) and the programmer needs to check that (this is arguably a bit less cluttered than using catch/3, and allows writing open(..., ..., ..., Stream, ok) to cause failure if there’s an error - on the other hand, it’s probably best to wrap things with setup_call_cleanup/3). So, I prefer to throw an error rather than have an extra argument that unifies with the error result.

@ridgeworks will probably disagree with me (there was a long thread about this).

At this moment, I am inclined to agree with this thought, with an yet another similar
example arg/3, with index out of range call, which simply fails nicely for array

the thread that @peter.ludemann meant, most probably

That depends IMO. In this case, term_string/2 already raises a syntax error if the string is not valid Prolog syntax. That is, for larger terms, pretty useful for debugging. Having an extra argument rather than an exception seems a step back. It would require a lot of if-then-else to check this argument while you can catch exceptions at a higher level.

Not (never) having an exception for term_string/2 seems highly dubious to me. People tend to write failure driven loops processing a lot of strings. A failure will silently omit the invalid strings. I think omitting these should be a choice that is carefully considered.

The current GIT version raises a syntax_error(end_of_string) exception.

1 Like

Thanks. As your analysis, syntax error is better than failure for this case because there might be
possible serious error other than syntax error .

Quiet unexpected failures are probably the biggest debugging problem I have in Prolog. Features such as SSU and the det/1 directive have helped a lot with that; which is why I prefer errors to failure (this also might be because of my experience with Python, Java, C++).

An error is just failure with some additional information and a different syntax. If I need to process a failure, I write an “if-then-else” ->/2 (or add a clause); if I need to process an exception, I use catch/3 or setup_call_cleanup/3 (or both). In both cases, the handling can happen far away from where the original problem occurred. Logical purity is nice; but some impure pragmatism can help my non-logical brain write code that works (most of the time).

To hopefully make both camps content: how about adding a variant which has an additional options argument, so we could have:

term_string(T, "a(", options([fail_silently]))

This makes it explicit that the programmer is taking responsibility for the errors.

The default would be to raise an exception.

This could become a standard way to opt-out of exceptions handling.

Needless to say I have a different perspective; particularly with built-in predicates which often can’t be implemented in Prolog, or are built-in for efficiency reasons. Failure should always be preferred over errors except when continued execution isn’t possible, e.g., resource errors.

My perspective:

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.

No, the semantics are very different. Errors abort the current computation while failure causes the search for alternative solutions to continue; Prolog being non-deterministic allows for such a possibility. Now one can be turned into the other if you’re prepared add code (and can live with the added expense).

And I fear this is a common issue. Prolog is different and I think you get the most out of it if you’re prepared to abandon at least some of the “old ways”. But I’m not about to second guess anybody else’s choices (and this is a philosophical fight I weary of).

Unfortunately, I think this just leads to feature creep. Better to have the programmer wrap term_string in a predicate which implements their desired semantics for their application.

There was some discussion for the original standard about controlling this with an environment flag (O’Keefe IIRC) but this never made it to the final version. Global flags which modify program semantics are generally not a good idea.

“fails_silently” option might be useful for parsing a large text file with prolog terms
to know how many syntax error terms are there in the file without
interruptions. ( I am writing this comment tex’s nonstopmode option in mind, which
is of course useful option, I think.).

If we had an api for restarting or resuming reading then end_of_file makes a lot of sense.