Sometime term_string/2 confuses me, and it causes bugs into my codes. One of such confuses, I think, comes from that I believed that the length of “atom name” of the “atom” ‘A’ is 1. But, like an example below, the length of X returned by query term_string('A', X) is 3 ! Maybe ‘A’ itself is not an atom, but merely a prolog term to indicate a unique atom which has name ‘A’ as a term. Of course, practically I am satisfied with nice property that
term_string(X, Y), term_string(Z, Y) => X == Z.
How are you free from possible confusions about term_string('A', X) ?
?- atom_length('A', X).
X = 1.
?- string_length('A', X).
X = 1.
?- term_string('A', X).
X = "'A'".
?- term_string('A', X), string_length(X, L).
X = "'A'",
L = 3.
?- term_string('a', X).
X = "a".
?- term_string(a, X).
X = "a".
?- term_string("abc", X), term_string(Y, X).
X = "\"abc\"",
Y = "abc".
Perhaps you intended to use atom_string/2 and not term_string/2?
?- Atom='ABC', atom_string(Atom, X), atom_length(Atom, AtomLength), string_length(X, StringLength).
Atom = 'ABC',
X = "ABC",
AtomLength = StringLength, StringLength = 3.
In term_string('A', X), X gets a representation of the term 'A'. There’s an atom_length/2 predicate, but it only works with atoms, not with terms in general. The representation must work with all possible terms, so it needs to add quotes.
Note that term_string/2 works in both directions:
?- term_string('ABC', X).
X = "'ABC'".
?- term_string(Y, "'ABC'").
Y = 'ABC'.
Another way of thinking about it: you’re using term_string(Term, String) and expecting that term_length(Term) should be the same as string_length(String). But there is no term_length/1 – what would it mean in general (and not just for atoms or strings)?
Rather, 'A' is an atom, but since term_string/2 quotes if necessary, the string representation has a length of 3. From the docs:
Term is ‘written’ using the option quoted(true) and the result is converted to String.
Since this term has to be quoted it “increases” in length by exactly 2.
One way to think about it is that the second argument (the string) is just text that could be parsed into a Prolog term. It gets however increasingly confusing if you parse what would be a variable in Prolog text:
?- term_string(T, S).
S = "_27118". % conversion still went from term (variable) to string
?- term_string(T, "A").
true. % What happened?
?- term_string(T, "A"), display(T).
_31336 % OK, the text "A" was parsed into a Prolog term, a free variable
true.
?- term_string(T, 'A').
true.
?- term_string(T, 'A'), display(T).
_2788 % atoms are also text...
true.
?- term_string(T, "").
T = end_of_file.
?- term_string(T, '').
T = end_of_file. % okay
So indeed, the right argument is really just text. (But I am now more confused than before)
Annoyingly enough it works with all “atomic” it seems, not just atoms
?- atom_length("string", N).
N = 6.
?- atom_length(0, N).
N = 1.
?- atom_length(42, N).
N = 2.
?- atom_length([], N).
N = 0.
?- term_string([], S), atom_length(S, N).
S = "[]",
N = 2.
?- atom_length(22r7, N).
N = 4.
So the “empty list” is a non-atom atomic with an atom length of 0 that can be stringified, and the atom length of that string is 2 but that 2 is not coming from the quotes.
So what happens here really? Is the empty list interpreted as a code list with 0 length, converted to the empty atom, which then has a length of 0? I guess so.
It is a good suggestion for me. I agree at least as a prolog programmer. It sounds like you say the second argument text is the name of a prolog term in the first argument. This intuitive meaning will decrease related possible bugs in the future.
Well this is the problem with using natural language for describing computer programs I though more like “the second argument is a string that holds the text that would represent the term in the first argument”. Really not sure about “name of a prolog term”. I know that some long time ago “name” was a thing in Prolog programming, based on the existence of the (cautiously deprecated) name/2.
This shows the C implementation of atom_length/2 is in the src directory and pl-prims.c file.
A more efficient way I have found to search for such is to use Notepad++ to search a directory of local copies of GitHub repositories. A bit more details are in this reply.
Thanks for all comments . I was glad to hear them. Reading them I came to a practical conclusin about use of term_string/2 avoiding unexpeced confusions.
Given a prolog term X, term_string(X, Y) returns some string Y from which X can be restored in a variant term. I recommend here that one should not much pay attention to the exact form of the text Y, which may be a kind of implementation matter. Imortant thing is that using term_string one can save a term as Y in a file. After then
reading the text Y, the X is restored by term_string(X, Y).
Of couse, it is necessary to pay some minimum attention to the exact form Y when Y is sent to other languages e.g. Javasritpt.
?- X = f(A, B, A),
term_string(X, S), term_string(Y, S), variant(X, Y).
X = f(A, B, A),
S = "f(_9546,_9548,_9546)",
Y = f(_A, _, _A).
Be very careful with that. Practically all predicates do allow duplicate options, but there are a lot of ways to process options and some of these pick the first, while others pick the last. I think that eventually all should pick the first. That would be consistent with the Prolog library(options) and using a dict that has no duplicates and thus Opts.put(quoted,false) creates a new option dict where quoted=false. Notably the built-in option processing helper picks the last, as well as most “hand coded” option processing in C(++) that walks over the list and processes the options one by one.
Yes, as I said, most C code picks the last, most Prolog code picks the first. The last is nice for adding defaults to options, while the first is nice to make sure some option value is used regardless of the options coming from the environment. Both clearly have use cases … It would definitely be better if all option processing was consistent.