`string_codes/2` works on lists given as the first argument?

Accoring to queries, string_codes/2 seems work also for the case in which the first argument is a list. Is this consistent with the help or a bug ?

 string_codes(?String, ?Codes)` 
		Bi-directional conversion between a string 
		and a list of character codes. At
		least one of the two arguments must be instantiated.
?- string_codes("abc", X).
X = [97, 98, 99].

?- string_codes(`abc`, X).
X = [97, 98, 99].

?- string_codes([a,b,c], R).
R = [97, 98, 99].

?- string_codes([1,2,3], R).
R = [1, 2, 3].

?- is_list(`abc`).
true.

?- string(`abc`).
false.

?- string(`abc`).
false.

Like many other predicates with a “string/list of character” argument it is on purpose quite permissive. Try something like

?- string_codes([-1], Codes).

PS: in the intro of the section on strings in the docs,

In general, SWI-Prolog’s text manipulation predicates accept any form of text as input argument - they accept anytext input. anytext comprises…

Here: SWI-Prolog -- Predicates that operate on strings

PPS: to my dismay, this works, as promised in the docs:

?- string_codes(-1, Codes).

Super useful!

lt said “type error”. So I guess string_codes/2 allows a list whose element is character code or an atom atom-length of which is 1.

Although I am not sure about merit of such being permissive, string_codes/2 should return type error at least for compound terms due to the help description.

I am sorry that I confusingly edited my comment twice with more info. The intro to the section in the docs explains it, the anytext concept. I mean this one:

In general, SWI-Prolog’s text manipulation predicates accept any form of text as input argument - they accept anytext input. anytext comprises…

I would guess that applies for other string predicates, too.

1 Like

It’s new for me. Thanks. Although I could not imagine situations where it is so useful, but after testing following similar queries now I feel I see Prolog’s natural mind to see atomic objects. That is, like atoms, every atomic object in Prolog has codes as its name (but not necessarily unique). Viewing a text as a series of such abstract letters in a restricted set, I expect help descriptions on textual operations like atomics_to_string would sound rather to be precise description.

( Some queries return error).

?- string_codes(-1, X).
?- string_codes(-2, X).
?- string_codes(-3, X).
?- string_codes(-3.0, X).
?- string_codes(-3.0.0, X).
?- string_codes(-3.3, X).
?- string_codes(-3.0.0, X).
?- string_codes(-03.4, X).
?- string_codes(-1111111111111111111111111111111111111111111111111111111111111.1, X).
?- string_codes(0'1, X).
?- string_codes(0'1, X), string_codes(Y, X).
?- string_codes(49, X).
?- string_codes(-3.6e123,X).
?- string_codes(-3.6g123,X).
?- string_codes(-12.34e567, X).
?- string_codes(-12.34e5, X), string_codes(Y, X).

Being inspired by the example query ?- string_codes( -1, Codes)., I have introduced a tiny predicate smash_string/2 as below for my purpose related to my cgi in prolog, though it is now more than 10 years (obsolete) behind the current cgi programing level with which I am not familiar. I noticed that the old style cgi of mine in prolog had naturally broken in many ways, so I spent time to fix it them all for the first time.

Now I replaced output (call-back ?) predicate with the smash_string/2, and checked it works.

% ?- smash_string(-1, X).
% ?- smash_string([-12e-1], X).
%@ X = "-1.2".
% ?- smash_string(-12e-1, X).
%@ X = "-1.2".
% ?- smash_string(sin(-12e-1), X).
%@ X = "sin(-1.2)".

smash_string(X, Y):- smash_to_atomics(X, Z, []),
	atomics_to_string(Z, Y).

% ?- smash_to_atomics([a,[],"b"+c], R,[]).
%@ R = [a, "\"b\"+c"].
% ?- smash_to_atomics([a,[97], b+c], R,[]).
%@ R = [a, 97, "b+c"].

smash_to_atomics([], A, A):-!.
smash_to_atomics([X|Y], A, B):-!, smash_to_atomics(X, A, C),
	smash_to_atomics(Y, C, B).
smash_to_atomics(X, [Y|A], A)	:- compound(X), !, term_string(X, Y).
smash_to_atomics(X, [X|A], A).	% X must be atomic here.