# term\_string('A', "'A'")

**URL:** <https://swi-prolog.discourse.group/t/term-string-a-a/5516>\
**Category:** Help!\
**Tags:** discussion\
**Created:** [June 18, 2022, 7:56pm UTC](https://swi-prolog.discourse.group/t/term-string-a-a/5516 "2022-06-18T19:56:56Z")\
**Posts on this page:** 16\
**Page:** 1

<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:** [June 18, 2022, 7:56pm UTC](https://swi-prolog.discourse.group/t/term-string-a-a/5516/1 "2022-06-18T19:56:56Z")

</div>

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

```prolog
term_string(X, Y), term_string(Z, Y) => X == Z.

```

How are you free from possible confusions about `term_string('A', X)` ?

```prolog

?- 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".

```

---

<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:** [June 19, 2022, 7:08am UTC](https://swi-prolog.discourse.group/t/term-string-a-a/5516/2 "2022-06-19T07:08:48Z")

</div>

Perhaps you intended to use atom\_string/2 and not term\_string/2?

```prolog
?- 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:

```prolog
?- 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)?

---

<div class="post-metadata">

**Author:** ![Boris](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/boris/32/7486_2.png) [@Boris](https://swi-prolog.discourse.group/u/Boris)\
**Post date:** [June 19, 2022, 7:49am UTC](https://swi-prolog.discourse.group/t/term-string-a-a/5516/3 "2022-06-19T07:49:19Z")

</div>

> [@kuniaki.mukai](#):
>
> Maybe ‘A’ itself is not an atom, but merely a prolog term to indicate a unique atom which has name ‘A’ as a term.

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.

> [@kuniaki.mukai](#):
>
> How are you free from possible confusions about `term_string('A', X)` ?

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:

```prolog
?- 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)

---

<div class="post-metadata">

**Author:** ![Boris](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/boris/32/7486_2.png) [@Boris](https://swi-prolog.discourse.group/u/Boris)\
**Post date:** [June 19, 2022, 7:54am UTC](https://swi-prolog.discourse.group/t/term-string-a-a/5516/4 "2022-06-19T07:54:09Z")

</div>

> [@peter.ludemann](#):
>
> There’s an [atom\_length/2](https://www.swi-prolog.org/pldoc/doc_for?object=atom_length/2) predicate, but it only works with atoms, not with terms in general.

Annoyingly enough it works with all “atomic” it seems, not just atoms 🙂

```prolog
?- 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**.

This is too meta for me.

---

<div class="post-metadata">

**Author:** ![Boris](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/boris/32/7486_2.png) [@Boris](https://swi-prolog.discourse.group/u/Boris)\
**Post date:** [June 19, 2022, 8:01am UTC](https://swi-prolog.discourse.group/t/term-string-a-a/5516/5 "2022-06-19T08:01:14Z")

</div>

> [@Boris](#):
>
> ```prolog
> ?- atom_length([], N).
> N = 0.
> 
> ```

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.

---

<div class="post-metadata">

**Author:** ![oskardrums](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/oskardrums/32/3594_2.png) [@oskardrums](https://swi-prolog.discourse.group/u/oskardrums)\
**Post date:** [June 19, 2022, 8:29am UTC](https://swi-prolog.discourse.group/t/term-string-a-a/5516/6 "2022-06-19T08:29:02Z")

</div>

> [@Boris](#):
>
> 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.

That seems to be the case, atom\_length/2 delegates to the C function PL\_get\_text which has a special case for the empty list:

```prolog
int
PL_get_text(DECL_LD term_t l, PL_chars_t *text, int flags)
{ word w = valHandle(l);
  if ( (flags & CVT_ATOM) && isAtom(w) )
  { if ( isNil(w) && (flags&CVT_LIST) )
      goto case_list;
...
  case_list:
    if ( (b = codes_or_chars_to_buffer(l, BUF_STACK, FALSE, &result)) )
    { text->length = entriesBuffer(b, char);
      addBuffer(b, EOS, char);
      text->text.t = baseBuffer(b, char);
      text->encoding = ENC_ISO_LATIN_1;
    }
...
}

```

Where `codes_or_chars_to_buffer` just returns an empty buffer when given the empty list as the first argument AFAICT.

---

<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:** [June 19, 2022, 8:49am UTC](https://swi-prolog.discourse.group/t/term-string-a-a/5516/7 "2022-06-19T08:49:35Z")

</div>

> [@Boris](#):
>
> 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 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.

---

<div class="post-metadata">

**Author:** ![Boris](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/boris/32/7486_2.png) [@Boris](https://swi-prolog.discourse.group/u/Boris)\
**Post date:** [June 19, 2022, 8:55am UTC](https://swi-prolog.discourse.group/t/term-string-a-a/5516/8 "2022-06-19T08:55:56Z")

</div>

> [@kuniaki.mukai](#):
>
> It sounds like you say the second argument text is the name of a prolog term in the first argument.

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.

---

<div class="post-metadata">

**Author:** ![EricGT](https://avatars.discourse-cdn.com/v4/letter/e/f1d935/32.png) [@EricGT](https://swi-prolog.discourse.group/u/EricGT)\
**Post date:** [June 19, 2022, 8:57am UTC](https://swi-prolog.discourse.group/t/term-string-a-a/5516/9 "2022-06-19T08:57:43Z")

</div>

FYI for those wondering how to track down source code when it passes from Prolog to C.

* * *

Normally to see the code behind a documented predicate just go to the SWI-Prolog documentation page and click on ![image](https://global.discourse-cdn.com/free1/uploads/swiprolog/original/2X/e/e8c92e7d74330724c8bf388349b8273868ae5993.png)

E.g.

For append/2, ![image](https://global.discourse-cdn.com/free1/uploads/swiprolog/original/2X/e/e8c92e7d74330724c8bf388349b8273868ae5993.png) links to [append/2 source code](https://www.swi-prolog.org/pldoc/doc/_SWI_/library/lists.pl?show=src#append/2)

Note: ![image](https://global.discourse-cdn.com/free1/uploads/swiprolog/original/2X/e/e8c92e7d74330724c8bf388349b8273868ae5993.png) is at the right of  
 ![image](https://global.discourse-cdn.com/free1/uploads/swiprolog/original/2X/b/b6d63c48c2bc9b0d064d30812069fbb461cc6a04.png)

* * *

However for atom\_length/2 it shows.

![image](https://global.discourse-cdn.com/free1/uploads/swiprolog/original/2X/7/71a0e909e452ede0d3aaa6f9bb2c529e6020e571.png)

So the documentation will not help at this point the source code has to be searched.

The source code is on GitHub with two repositories for SWI-Prolog

- Dev - [GitHub - SWI-Prolog/swipl-devel: SWI-Prolog Main development repository](https://github.com/SWI-Prolog/swipl-devel)
- Stable - [GitHub - SWI-Prolog/swipl: SWI-Prolog stable releases](https://github.com/SWI-Prolog/swipl)

Typically ones SWI-Prolog version is in sync with the latest SWI-Prolog development release so that repository will be used.

Browse to the [GitHub repository for SWI-Prolog development](https://github.com/SWI-Prolog/swipl-devel).  
(`https://github.com/SWI-Prolog/swipl-devel`)

In the upper left search box  
 ![image](https://global.discourse-cdn.com/free1/uploads/swiprolog/original/2X/2/2bbcd5201d8605a7b266e58b7df749edbbb46c40.png)

enter the search word: `atom_length` and press enter.

 ![image](https://global.discourse-cdn.com/free1/uploads/swiprolog/original/2X/c/c3862d6dd1fdf0fc503feb6f724c53f4a6cfcb3f.png)

On the left at the bottom click `Advanced search`

 ![image](https://global.discourse-cdn.com/free1/uploads/swiprolog/original/2X/c/c4b2b354379ce04cabd886ad7819a845b61064a3.png)

For `Written in this language` select: `C`

 ![image](https://global.discourse-cdn.com/free1/uploads/swiprolog/original/2X/0/025182f9385c0f022f6766b79c386df938d9b9b4.png)

Click `Search`

 ![image](https://global.discourse-cdn.com/free1/uploads/swiprolog/original/2X/f/fdcfe6abeccfbb3e587dd76abc988dbe8d8665cf.png)

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](https://swi-prolog.discourse.group/t/having-trouble-using-multifile-1-for-multi-file-predicates/4460/4).

---

<div class="post-metadata">

**Author:** ![Boris](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/boris/32/7486_2.png) [@Boris](https://swi-prolog.discourse.group/u/Boris)\
**Post date:** [June 19, 2022, 9:07am UTC](https://swi-prolog.discourse.group/t/term-string-a-a/5516/10 "2022-06-19T09:07:10Z")

</div>

For the impatient, you could also use git grep with a path specification for “C files only”:

```prolog
$ git grep atom_length -- *.c
src/pl-prims.c:PRED_IMPL("atom_length", 2, atom_length, PL_FA_ISO)
src/pl-prims.c: PRED_DEF("atom_length", 2, atom_length, PL_FA_ISO)

```

No idea about the subtleties of quoting the path specification on different shells/OSs though ☹

---

<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:** [June 19, 2022, 10:44am UTC](https://swi-prolog.discourse.group/t/term-string-a-a/5516/11 "2022-06-19T10:44:52Z")

</div>

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.

```prolog
?- 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).

```

---

<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:** [June 19, 2022, 5:42pm UTC](https://swi-prolog.discourse.group/t/term-string-a-a/5516/12 "2022-06-19T17:42:12Z")

</div>

> [@kuniaki.mukai](#):
>
> Given a prolog term `X`, `term_string(X, Y)` returns some string `Y` from which `X` can be restored in a variant term

If you want to be sure that the term can be restored, you should use some extra options. I _think_ that these suffice:

```prolog
?- term_string(ハロー+'Foo', X, [ignore_ops(true), quoted(true), quote_non_ascii(true)]).
X = "+('ハロー','Foo')".

```

---

<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:** [June 19, 2022, 6:56pm UTC](https://swi-prolog.discourse.group/t/term-string-a-a/5516/13 "2022-06-19T18:56:33Z")

</div>

> [@peter.ludemann](#):
>
> ` [ignore_ops(true), quoted(true), quote_non_ascii(true)]).`

Thanks for the remark. I seldom saw such options, but I tested with cut and paste. The result is exactly as you said. It is impressive.

```prolog
?- write_canonical(ハロー+'Foo').
+('ハロー','Foo')
true.

?- X = ハロー+'Foo',
| Options=[ignore_ops(true), quoted(true), quote_non_ascii(true)],
| term_string(X, Y, Options), term_string(Z, Y, Options),
| X=Z.
X = Z, Z = ハロー+'Foo',
Options = [ignore_ops(true), quoted(true), quote_non_ascii(true)],
Y = "+('ハロー','Foo')".

```

---

<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:** [June 22, 2022, 9:28am UTC](https://swi-prolog.discourse.group/t/term-string-a-a/5516/15 "2022-06-22T09:28:29Z")

</div>

> [@anon95304481](#):
>
> Its interesting that [SWI-Prolog](https://www.swi-prolog.org/pldoc/doc_for?object=manual) and most other Prolog systems allow option  
> merging already through the option list itself:

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.

---

<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:** [June 22, 2022, 9:39am UTC](https://swi-prolog.discourse.group/t/term-string-a-a/5516/17 "2022-06-22T09:39:22Z")

</div>

> [@anon95304481](#):
>
> seems to pick the last, in case of quoted/1 ?

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.

---

<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:** [June 22, 2022, 10:56am UTC](https://swi-prolog.discourse.group/t/term-string-a-a/5516/19 "2022-06-22T10:56:20Z")

</div>

> [@anon95304481](#):
>
> Maybe the easiest to unterstand [term\_string/2](https://www.swi-prolog.org/pldoc/doc_for?object=term_string/2) with mode (+,-) is  
> to compare it with [write\_term/2](https://www.swi-prolog.org/pldoc/doc_for?object=write_term/2).

I see. In other words, `term_string/2` is an inverse function of `read` (as function from strings to terms).  
Thanks.
