scryer-prolog icon indicating copy to clipboard operation
scryer-prolog copied to clipboard

Goal expansion for arguments of (',')//2

Open triska opened this issue 1 year ago • 7 comments

In my application, I define a goal expansion to replace every invocation of a//0 by that of b//0. For instance, consider the following definitions:

:- use_module(library(dcgs)).

user:goal_expansion(a(Cs0,Cs), b(Cs0,Cs)).

:- dynamic(p/2).

:- meta_predicate(g(2,?,?)).

p --> a.
p --> g(a).
p --> g(g(a)).
p --> g((g(a),[])).

Yielding:

?- clause(p(Cs0, Cs), Body).
   Body = b(Cs0,Cs)
;  Body = g(user:b,Cs0,Cs)
;  Body = g(user:g(user:b),Cs0,Cs)
;  Body = g(user:(g(a),[]),Cs0,Cs), unexpected.

The first 3 answers work completely as expected, with b appearing instead of a in Body. But in the last answer, g(a) appears unexpectedly, where we would expect g(b).

triska avatar Mar 20 '24 23:03 triska

Analogously for (;)//2, i.e., if I write for example:

p --> g((a;[])).

triska avatar Mar 20 '24 23:03 triska

I have filed #2367 for this, and it seems to address the issue. Is this change correct? Thank you a lot!

triska avatar Mar 21 '24 20:03 triska

Very nice, I'd just started to look at this.

mthom avatar Mar 21 '24 20:03 mthom

Unfortunately, #2367 in fact did not solve the issue. But, strangely, when I add the very same meta_predicate/1 declaration to the program itself, then it works as expected:

:- use_module(library(dcgs)).

:- meta_predicate(','(2, 2, ?, ?)).

user:goal_expansion(a(Cs0,Cs), b(Cs0,Cs)).

:- dynamic(p/2).

:- meta_predicate(g(2,?,?)).

p --> a.
p --> g(a).
p --> g(g(a)).
p --> g((g(a),[])).

yielding:

?- clause(p(Cs0, Cs), Body).
   Body = b(Cs0,Cs)
;  Body = g(user:b,Cs0,Cs)
;  Body = g(user:g(user:b),Cs0,Cs)
;  Body = g(user:(user:g(user:b),user:[]),Cs0,Cs).

triska avatar Mar 28 '24 19:03 triska

How does a/0 become b/0 when the goal expansion is a/2 to b/2? SWI-Prolog does this but Yap leaves a/0 as-is.

EDIT: never mind, ignore this. The '2' specifies extra closure arguments that must be taken into account and that makes it equivalent to a/2.

infradig avatar Mar 31 '24 23:03 infradig

This happens because meta-predicate definitions are hidden behind modules. The user module is global but all other modules, including dcgs, are local, so they don't inform goal expansion at the level of the user module. Of course, once predicates are exported, (e.g. for phrase/{2,3,4,5}) their local meta-predicate definitions are exported too.

mthom avatar Apr 01 '24 23:04 mthom

Interestingly, there is a fundamentally different way to go about implementing DCGs that I think would solve this specific issue: library(dcgs) could define and export predicates (',')/4, (->)/4 etc. This is the approach used by Yap:

https://github.com/vscosta/yap-6.3/blob/master/pl/grammar.yap#L28

With these Prolog predicates that directly correspond to grammar control constructs, phrase/N for compound goals can be implemented as a direct call:

https://github.com/vscosta/yap-6.3/blob/2e6528994219055d05394e64726b4bf6835104a3/pl/grammar.yap#L251

This implementation approach could be worth considering also for Scryer, especially if someone is interested in working on this and wants to compare the advantages of each variant.

triska avatar Apr 07 '24 09:04 triska