Currently experimenting with this guard, might be more
complicated for modules. Where does this guard come from?
Derived from ISO body conversion algorithm:
sys_trans_allowed(V) :- var(V), !, fail.
sys_trans_allowed((A,B)) :- sys_trans_allowed(A), sys_trans_allowed(B).
sys_trans_allowed((A;B)) :- sys_trans_allowed(A), sys_trans_allowed(B).
sys_trans_allowed((A->B)) :- sys_trans_allowed(A), sys_trans_allowed(B).
sys_trans_allowed(A) :- callable(A).
The idea of sys_trans_allowed/1 is to check whether the given
goal is âtroublesomeâ or not. A similar check that leads Ciao Prolog
to display a warning, only we do not check for the presence of cut (!)/1.
Rather the check is for naked variables that would get the call/1
treatment. Now if sys_trans_allowed(A)
holds, you can perform
these transformations:
/* Basically Done in SWI-Prolog */
\+ A ~~> (A -> fail; true)
/* Not available in SWI-Prolog, unlike other Prolog Systems */
once(A) ~~> (A -> true; fail)
Transformations from outside to inside. I get these result in
SWI-Prolog, observe that for A = p(X) the predicate sys_trans_allowed/1
holds and obsever that SWI-Prolog does not inline once/1:
test(1, X) :- \+ p(X).
test(2, X) :- once(p(X)).
?- vm_list(test/2).
/* \+ p(X) */
3 c_not(2,'L1')
6 b_var1
7 i_call(user:p/1)
9 c_cut(2)
11 c_fail
L1: 12 i_exit
/* once(X) */
3 b_functor(p/1)
5 b_argvar(1)
7 b_pop
8 i_depart(system:once/1)
10 i_exit
The above is fine, now consider a case where sys_trans_allowed/1
does not hold. Use A = (X,Y,Z) again. I get these results, SWI-Prolog
insists to still inline (\+)/2
:
test2(1,X,Y,Z) :- \+ (X,Y,Z).
test2(2,X,Y,Z) :- once((X,Y,Z)).
?- vm_list(test2/4).
/* \+ (X,Y,Z) */
3 c_not(4,'L1')
6 b_var1
7 i_usercall0
8 b_var2
9 i_usercall0
10 b_var(3)
12 i_usercall0
13 c_cut(4)
15 c_fail
L1: 16 i_exi
/* once((X,Y,Z)) */
3 b_functor((',')/2)
5 b_argvar(1)
7 b_rfunctor((',')/2)
9 b_argvar(2)
11 b_argvar(3)
13 b_pop
14 i_depart(system:once/1)
16 i_exit
Now observe that once/1 doesnât show a discrepancy between
inside a clause and inside a query. I get this result, no bug for
once/1 but a bug for (\+)/1
:
p(a).
p(b).
q(b).
/* Bug in (\+)/1 */
?- \+ (p(X),!,q(X)).
true.
?- test2(1,p(X),!,q(X)).
false.
/* No Bug in once/1 */
?- once((p(X),!,q(X))).
false.
?- test2(2,p(X),!,q(X)).
false.
The consistent result is because once/1
isnât inlined. So you could
fix the bug by not always inlining (\+)/1
or then inline it through some
approach that preserves semantics.