Double_quotes flag and DCGs

Through trial and error I’ve arrived at the opinion that setting the double_quotes flag to chars is a bad idea when working with DCGs. It seems to break the functionality of the predicates defined in the dcg/basics library. Before working with DCGs I thought it was recommended (maybe by Markus Triska) to make chars the default setting. But am I correct that that’s not a good idea when using DCGs?

1 Like

Yes, indeed, you need a very good reason to set double_quotes to chars in SWI-Prolog. Anecdotally, I have never needed this setting. Why exactly it has become so popular to do that is difficult to understand.

Keep in mind that while double-quoted literals are strings in SWI-Prolog, in the context of DCGs they work as intended. In other words, if you leave the flags at their default setting, those two both work:

foo --> "bar".

and

foo --> `bar`.

and they mean the same. But of course the first one is the usual way to define DCGs. This is documented in the same section, where it says:

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.

2 Likes

It is nice for little student exercises on the terminal. At least I think that is the main reason. There is some value of chars over codes as they are easier to read, notably in the debugger. That is why there is the portray_text/1 predicate. Still, chars would have made this more easy as recognizing a list of integers as text is a weaker heuristic than recognizing a list of one-character atoms as text.

But, chars and codes do not go well together. Libraries are (often) written for either and thus the libraries make the choice. As codes are historically used by Prolog systems in the Edinburgh/Quintus tradition, it is hard to switch :frowning: Finally, codes are probably a little faster and sometimes you can do meaningful arithmetic on them.

2 Likes

The default value of the double_quotes flag is string, but I have to change it to codes for the following simple DCG example to work.

:- set_prolog_flag(double_quotes, codes).

% This gathers a sequence of characters into a list of character atoms.
seq([]) --> [].
seq([H|T]) --> [H], seq(T).

% This gathers a sequence of characters into a string.
string_seq(S) --> seq(Cs), { atom_codes(S, Cs) }.

hello(Name) --> "Hello, ", string_seq(Name), "!".

I can use this in a REPL session like this:

?- once(phrase(hello(Name), "Hello, World!")).
Name = 'World'.

This breaks if I leave double_quotes set to string.
Is there a different way I need to write these DCG rules so it works in that case?

You don’t need to change the definition, but you do need to change the call:

?- once(phrase(hello(Name), `Hello, World!`)).
Name = 'World'.

Within a DCG rule definition, SWI-Prolog will do the right thing with double-quoted literals, as discussed above. However, the second argument of phrase/2 has to be a list of codes, so you need to quote it in backticks :man_shrugging:t2: Other than one-line examples on the top-level, you rarely will be typing in your input by hand so this is not a huge issue in my experience.

(Footnote: anecdotally, I only use phrase/2 for trying out manually on the top-level. In my code I usually need phrase_from_file/2 and phrase_from_stream/2.)

All this said, string//1 from library(dcg/basics) is what you’d be using in SWI-Prolog (or maybe string_without//2?). The definition of seq//1 as in your example is not necessary. The docs show the more usual place for the cut: not wrapping the call to phrase/2, but right after your delimiter within the parser. You will find the examples in the docs but I will copy them here for posterity.

:- use_module(library(dcg/basics)).

hello_string(Name) -->
    "Hello, ", string(S), "!", !, % you probably should cut here, not outside!
    { atom_codes(S, Name) }.

hello_string_without(Name) -->
    "Hello, ", string_without("!", S), "!", % you don't need the cut!
    { atom_codes(Name, S) }.

It is a very big topic where to cut, I don’t want to go into it…

2 Likes

@Boris Thank you so much! This was very helpful!

Note that operators such as -> work as expected in DCGs, so this could be rewritten (untested):

hello_string(Name) -->
    "Hello, "
    (  string(S), "!",
    -> []  % or {true}
    ;  string_without("!", S)
    ),
    { atom_codes(Name, S) }.

This was meant as an exclusive or, either use string//1, or use string_without//2.

What am I missing :sweat_smile: