While experienced Prolog programmers have learned to cope with this, we still consider this an unfortunate situation.
We observe that in many programs, most strings are only handled as a single unit during their lifetime. Examining real code tells us that double quoted strings typically appear in one of the following roles:
- A DCG literal
- Although represented as a list of codes is the correct representation for handling in DCGs, the DCG translator can recognise the literal and convert it to the proper representation. Such code need not be modified.
- A format string
- This is a typical example of text that is conceptually not a program identifier. Format is designed to deal with alternative representations of the format string. Such code need not be modified.
- Getting a character code
- The construct
[X] = "a"is a commonly used template for getting the character code of the letter’a'. ISO Prolog defines the syntax
0'afor this purpose. Code using this must be modified. The modified code will run on any ISO compliant Prolog Processor.
- As argument to list predicates to operate on strings
- Here, we might see code similar to
append("name:", Rest, Codes). Such code needs to be modified. In this particular example, the following is a good portable alternative:
phrase("name:", Codes, Rest)
- Checks for a character to be in a set
- Such tests are often performed with code such as this:
memberchk(C, "~!@#$"). This is a rather inefficient check in a traditional Prolog system because it pushes a list of character codes cell-by-cell onto the Prolog stack and then traverses this list cell-by-cell to see whether one of the cells unifies with C. If the test is successful, the string will eventually be subject to garbage collection. The best code for this is to write a predicate as below, which pushes nothing on the stack and performs an indexed lookup to see whether the character code is in‘my_class'.
my_class(0'~). my_class(0'!). ...
An alternative to reach the same effect is to use term expansion to create the clauses:
term_expansion(my_class(_), Clauses) :- findall(my_class(C), string_code(_, "~!@#$", C), Clauses). my_class(_).
Finally, the predicate string_code/3 can be exploited directly as a replacement for the memberchk/2 on a list of codes. Although the string is still pushed onto the stack, it is more compact and only a single entity.
The predicates in this section can help adapting your program to the new convention for handling double quoted strings. We have adapted a huge code base with which we were not familiar in about half a day.
- This predicate may be used to assess compatibility issues due to the
representation of double quoted text as string objects. See
section 5.2 and section
5.2.3. To use it, load your program into Prolog and run list_strings/0.
The predicate lists source locations of string objects encountered in
the program that are not considered safe. Such string need to be
examined manually, after which one of the actions below may be
- Rewrite the code. For example, change
[X] = "a"into
X = 0'a.
- If a particular module relies heavily on representing strings as
lists of character code, consider adding the following directive to the
module. Note that this flag only applies to the module in which it
:- set_prolog_flag(double_quotes, codes).
- Use a back quoted string (e.g.,
`text`). Note that this will not make your code run regardless of the --traditional command line option and code exploiting this mapping is also not portable to ISO compliant systems.
- If the strings appear in facts and usage is safe, add a clause to the multifile predicate check:string_predicate/1 to silence list_strings/0 on all clauses of that predicate.
- If the strings appear as an argument to a predicate that can handle string objects, add a clause to the multifile predicate check:valid_string_goal/1 to silence list_strings/0.
- Rewrite the code. For example, change
- Declare that PredicateIndicator has clauses that contain
strings, but that this is safe. For example, if there is a predicate
help_info/2 , where the second argument contains a double quoted string
that is handled properly by the predicates of the applications' help
system, add the following declaration to stop
:- multifile check:string_predicate/1. check:string_predicate(user:help_info/2).
- Declare that calls to Goal are safe. The module qualification
is the actual module in which Goal is defined. For example, a
call to format/3
is resolved by the predicate system:format/3.
and the code below specifies that the second argument may be a string
(system predicates that accept strings are defined in the library).
:- multifile check:valid_string_goal/1. check:valid_string_goal(system:format(_,S,_)) :- string(S).