Progress notes on text_max

I have implemented portable(true), max_text(+Length)andtruncated(-Bool)`. Some remarks:

  • I renamed text_max(+Length) to max_text(+Length) because all Prolog options and flags are max_*. Will be renamed to whatever the consensus will be.
  • It now prints prefix…suffix, where the suffix length is min((Max-AtomLen-EllipsisLen)//2, 3) and the prefix length takes the remaining characters. These are some remarks
    • If the streams allows for it, we use Unicode U-2026 (…), otherwise ASCII ...
    • If Length > 0 but < length(ellipsis) we take length(ellipsis). So, max_text(1) always just prints the ellipsis.
    • For non-quoted text we are now done.
    • For quoted text,
      • The quotes are not included in Length.
      • The total print length may exceed Length due to escape sequences. I.e., we compute the prefix and suffix lengths according to the above and then print using escape sequences where required.
      • If an atom needs no quotes, but its length exceeds Length, it will be quoted.

truncated(-Bool) is set to true if either or both max_depth or max_text are exceeded.

Here are some tests:

:- begin_tests(max_text).

test(string, Out == "\"hello\"") :-
	max_text(5, "hello", Out, [quoted(true)]).
test(string, Out == "\"hel…rld\"") :-
	max_text(7, "hello world", Out, [quoted(true), truncated(Bool)]),
	assertion(Bool == true).
test(string, Out == "\"…\"") :-
	max_text(1, "hello world", Out, [quoted(true)]).
test(string, Out == "\"h…\"") :-
	max_text(2, "hello world", Out, [quoted(true)]).
test(string, Out == "\"h…d\"") :-
	max_text(3, "hello world", Out, [quoted(true)]).
test(string, Out == "\"hel…ιος\"") :-
	max_text(7, "hello υφήλιος", Out, [quoted(true)]).
test(atom, Out == "'hel…ιος'") :-
	max_text(7, 'hello υφήλιος', Out, [quoted(true)]).
test(atom, Out == "hello_υφήλιος") :-
	max_text(20, hello_υφήλιος, Out, [quoted(true)]).
test(atom, Out == "'hel…ιος'") :-
	max_text(7, hello_υφήλιος, Out, [quoted(true)]).
test(atom, Out == "'\\nhe…ιος'") :-
	max_text(7, '\nhello_υφήλιος', Out, [quoted(true)]).
test(atom, Out == "hel…ιος") :-
	max_text(7, hello_υφήλιος, Out, [quoted(false)]).

max_text(Max, Term, String, Options) :-
	with_output_to(string(String),
		       write_term(Term, [max_text(Max)|Options])).

:- end_tests(max_text).

Prolog flags make a poor precedent because they are already inconsistent (see float_max, path_max vs max_integer).
We have a chance here to make at least the write-options internally consistent by following a <type>_<property> pattern. In that spirit, the PIP already contains float_precision, float_format, integer_base, integer_prefix, atom_quoting, character_escapes etc.[1]

This is quite different from what the PIP draft proposes, which was simply “print Max characters from the atom/string and an abbreviation marker for the rest”.
Your more complex rule seems motivated by the desire to control output width. Controlling output width properly is very difficult – subtracting the ellipsis is only one aspect, and you mention the issue of whether to count the quotes, and how to account for escape sequences, not to speak of variable width fonts.
Fortunately, controlling output width is not the job of write_term/2[2]. For that we have format/2.
I would therefore suggest to interpret the text_max setting here as controlling the “precision” to which a string is printed (measured in the number of string characters used), in the same way that float_precision controls the number of printed digits for floats.

Quotes suggest that whatever is quoted is to be taken literally. Abbreviation conflicts with that expectation anyway. But adding extra quotes in the very case where you have destroyed the precision by abbreviating, feels just the wrong way round! It might make more sense to remove quotes when abbreviating.


  1. max_depth should be seen as a shorthand for term_max_depth ↩︎

  2. where strings likely appear embedded in more complex terms ↩︎

Interesting. I did some searches. The ISO core standard seems to have only max-something. The SWI-Prolog source has pretty much everything this way also. max-references that are imported from external (C) sources mostly use something-max. float_max as Prolog flag is your invention and I don’t recall where the path_max flag comes from (most likely the POSIX C macro PATH_MAX).

So, the core question is whether we should stick with the ISO style or jump to the C style. For what it is worth, ChatGPT thinks this:

Style Popularity today Typical ecosystems
max_xyz ★★★★☆ (more popular) Rust, Go, Java, modern C++
xyz_max ★★★☆☆ (still common but older) Legacy C APIs, POSIX-style constants

Yes, but I deliberately asked that to be implementation dependent. I do not want very long strings in debugger or toplevel output, but one is typically interested in the start as well as the end. That is what this does. It is not my proposal to change the PIP, just report on an alternative that should (IMO) be allowed by the PIP.

I’m inclined to agree. At least it is much simpler to implement :slight_smile: As I do not account for the quotes and escape sequences, not accounting for the ellipsis is consistent.

Yes. Still, the output of write_term/2 when using quoted(true) is valid input for read/1. I think that should be preserved. So, I think the following should be valid:

?- write_term(hello_world, [max_text(7), quoted(true)]).
'hell...rld'

Note that we have the truncated(-Bool) option to check whether max_depth or max_text has created an abbreviated version.

Pushed a patch for that. This did uncover another edge case: should it be possible to have strings/atoms printed as ... only? I’m inclined to make this possible. So, in my current implementation, max_text(0) just prints ... (or "...") and the new default -1 disables max_text.

The main motivation is that I’ve used logging and comparing logs between a good and bad version of a program various times to debug nasty things. If for some reason you are not interested in the concrete text writing as ... might be an option. That is also why I think that using quoted(true) the output should always be valid Prolog syntax, even if truncated.