I learned the hard way that with SWI-Prolog the location for the Prolog directive set_prolog_flag
matters in a source code file.
The only documentation I found of value about loading source code files with directives was in Loading Prolog source files
A directive is an instruction to the compiler. Directives are used to set (predicate) properties (see section 4.15), set flags (see set_prolog_flag/2) and load files (this section). Directives are terms of the form :- <term>.
Is there documentation for SWI-Prolog that covers loading of source code that notes if a directive applies to the entire file or depends on the location in the source code file?
Or is it that all lines loaded from a source code file are just a simple playing of statements into the top-level and location always matters?
Supplement / TL;DR
Default
When using Definitive Clause Grammars (DCG) in Prolog it is known that DCG requires the input to be a list of character codes, e.g.
?- string_codes("abc123",Cs).
Cs = [97, 98, 99, 49, 50, 51].
and with the following DCG rule in a source code file and loaded into the top-level
digit(0) --> "0".
the DCG can be used with
?- string_codes("0",Cs),phrase(digit(D),Cs,R).
Cs = [48],
D = 0,
R = []
set_prolog_flag
Now to make it easier to use DCG instead of having to use string_codes
the Prolog directive
:- set_prolog_flag(double_quotes, chars).
can be used in a source code file and with the following DCG rule in a source code file and loaded into the top-level
digit(0) --> "0".
the DCG can be used with
?- phrase(digit(D),"0",R).
D = 0,
R = [].
That left out something important
It turns out that the if set_prolog_flag
appears before the DCG rule then skipping string_codes
works, but if set_prolog_flag
appears after the DCG rule then skipping string_codes
fails.
:- set_prolog_flag(double_quotes, chars).
digit(0) --> "0".
?- phrase(digit(D),"0",R).
D = 0,
R = [].
vs
digit(0) --> "0".
:- set_prolog_flag(double_quotes, chars).
?- phrase(digit(D),"0",R).
false.
The reasoning that led me afoul
While I am aware that a lot of programming with Prolog can be done in just the top-level, I tend to rely on source code files and consult/1.
In writing lots of code I started to use modules. With modules I found out that the Prolog flags are independent for each module.
?- current_prolog_flag(double_quotes,V).
V = string.
?- current_prolog_flag(symbolic:double_quotes,V).
V = string.
?- set_prolog_flag(symbolic:double_quotes,chars).
true.
?- current_prolog_flag(double_quotes,V).
V = string.
?- current_prolog_flag(symbolic:double_quotes,V).
V = chars.
and that the default top-level module is user
?- current_prolog_flag(double_quotes,V).
V = string.
?- current_prolog_flag(user:double_quotes,V).
V = string.
?- set_prolog_flag(double_quotes,chars).
true.
?- current_prolog_flag(double_quotes,V).
V = chars.
?- current_prolog_flag(user:double_quotes,V).
V = chars.
?- set_prolog_flag(user:double_quotes,codes).
true.
?- current_prolog_flag(double_quotes,V).
V = codes.
?- current_prolog_flag(user:double_quotes,V).
V = codes.
which lulled me into the false belief that the Prolog directive set_prlog_flag
applied to the entire module no matter where it was written.
What broke the mold
In writing lots of example code it was easier to keep all of the little examples in one file and associated with each little example was set_prolog_flag
. For an identifier example it needed two little example DCG rules, one for digit and one for letters. The digit rules were above the letter rules and working, but the letter rules had the set_prolog_flag
directive because I was working on them at the time. Remember I am thinking that the directive applies to the whole file at this point. Then in testing ident
the DCG rules for letters were working but the DCG rules for digits were failing.
digit(0) --> "0", !.
digit(1) --> "1", !.
digit(2) --> "2", !.
:- set_prolog_flag(double_quotes, chars).
ident(Id) --> letter(C), identr(Cs), { name(Id, [C|Cs]) }.
identr([C|Cs]) --> letter(C), !, identr(Cs).
identr([C|Cs]) --> digit(C), !, identr(Cs).
identr([]) --> [].
letter(a) --> "a", !.
letter(b) --> "b", !.
letter(c) --> "c", !.
?- phrase(ident(Id),"ab12",R).
Id = ab,
R = ['1', '2'].
Root cause
So using listing/1
?- listing(digit).
digit(0, [48|B], A) :- !,
A=B.
digit(1, [49|B], A) :- !,
A=B.
digit(2, [50|B], A) :- !,
A=B.
?- listing(ident).
ident(C, A, F) :-
letter(D, A, B),
identr(E, B, G),
name(C, [D|E]),
F=G.
?- listing(identr).
identr([A|D], B, F) :-
letter(A, B, C), !,
E=C,
identr(D, E, F).
identr([A|D], B, F) :-
digit(A, B, C), !,
E=C,
identr(D, E, F).
identr([], A, A).
?- listing(letter).
letter(a, [a|B], A) :- !,
A=B.
letter(b, [b|B], A) :- !,
A=B.
letter(c, [c|B], A) :- !,
A=B.
the problem was apparent
digit(0, [48|B], A) :- !,
A=B.
letter(a, [a|B], A) :- !,
A=B.
that digit was converted to use character codes 48
and letter was converted to use characters a
. That's when I asked myself if the location of set_prolog_flag
in source mattered.
Confirming root cause
To test this I created a little source code file
digit_before(0) --> "0".
:- set_prolog_flag(double_quotes, chars).
digit_after(0) --> "0".
and in top-level
?- current_prolog_flag(double_quotes,V).
V = string.
?- current_prolog_flag(symbolic:double_quotes,V).
V = string.
?- consult("C:/Users/Eric/Documents/Projects/Calculus Project/test.pl").
true.
?- current_prolog_flag(double_quotes,V).
V = chars.
?- current_prolog_flag(symbolic:double_quotes,V).
V = string.
?- listing(digit_before).
digit_before(0, [48|A], A).
true.
?- listing(digit_after).
digit_after(0, ['0'|A], A).
true
which confirmed that the Prolog directive set_prolog_flag
does not apply to an entire file. Notice that digit_before is converted to 48
and digit_after is converted to '0'
.
Notes
Note: The directive set_prolog_flag(F,V)
can also be used in the top-level and does not require the preceding :-
.
Note: The example used :- set_prolog_flag(double_quotes, chars).
but :- set_prolog_flag(double_quotes, codes).
also works. Using chars
value is preferred because it makes the values easier to read when debugging, etc.
letter(a)
in place ofletter('a')
. No need for quoting here, not even in SWI. – Dakar