Re: term_expansion/2 with DCG clauses

Paulo Moura <[email protected]>
Newsgroups gmane.comp.ai.prolog.swi
Message-ID <[email protected]>
Hi Michael,

On 15/08/2013, at 16:21, Michael Hendricks <[email protected]> wrote:

> On Thu, Aug 15, 2013 at 4:43 AM, Paulo Moura <[email protected]> wrote:
> As Richard would wrote, "An example would be handy about now." :-)
> 
> Good idea.  These conversations are always more productive when based around something concrete.
>  
> Assume that you have three expansions that should occur in a fixed order:
> 
>         A -> B -> C
> 
> ...
> Can you show us exactly how you would do the same (fixed order expansions) under recursive expansion?
> 
> If A, B and C macro terms don't unify with each other, then recursive expansion gives the proper order by default.  My experience suggests this is the most common scenario.  This works with goal_expansion today:
> 
>     goal_expansion(c, zap).  % C
>     goal_expansion(a, b).  % A
>     goal_expansion(b, c).  % B
> 
> 
> If A, B and C do unify with each other but they don't pollute the global user:term_expansion/2 predicate, one can do it the same as your Logtalk example. It behaves the same under recursive or first-solution expansion:
> 
>     :- use_module(a).
>     :- use_module(b).
>     :- use_module(c).

I don't see why the use_module/1 directives above would be required. But that's just a detail. The ideal would be, whenever possible, as you say above, to not pollute either "user" or "system".

>     term_expansion(T0, T) :-
>         a:term_expansion(T0, T1),
>         b:term_expansion(T1, T2),
>         c:term_expansion(T2, T).
> 
> 
> If A, B and C unify with each other and define global user:term_expansion/2, then each definition must cooperate to guarantee order and it gets a little ugly. I believe this is true under both recursive and first-solution expansion, because multifile predicates don't guarantee a clause order. (One of several reasons I don't like macro expansion via global predicates).  In this example, assume that mark_as_expanded/1 and has_expanded/1 use something like prolog_load_context(term_position,_) to uniquely identify terms.  Under recursive expansion it might look like this:
> 
>     % A
>     user:term_expansion(T0, T) :-
>         \+ has_expanded(T0),
>         % ... expand T0 into T
>         mark_as_expanded(T0).
>     % B
>     user:term_expansion(T0, T) :-
>         a:has_expanded(T0),
>         \+ has_expanded(T0),
>         % ... expand T0 into T
>         mark_as_expanded(T0).
>     % C
>     user:term_expansion(T0, T) :-
>         b:has_expanded(T0),
>         \+ has_expanded(T0),
>         % ... expand T0 into T
>         mark_as_expanded(T0).

Another ugly aspect is that now the expansions are no longer independent and must know about each other. Even worse if some of the extensions are third-party.

> Global resources have an unhappy way of requiring this sort of coordination, it seems.  Modules currently define user:term_expansion/2 because that's the only way to export macros.

That's one of the implicit points on the example on my previous mail: exporting macros should be the exception, not the rule. Take CHR as an example. From the SWI-Prolog docs, a file containing CHR code should include the directive:
 
:- use_module(library(chr)).

right after the module/2 directive. But this directive adds:

system:term_expansion(A, B) :-
    chr:
    (   \+ current_prolog_flag(xref, true),
	chr_expand(A, B)
    ).

So, despite my CHR code being encapsulated in a module (as advised), the code necessary for compiling it ends up in "system". My guess is that this is due to some of the details on how the term-expansion mechanism in SWI-Prolog works. But it should really not be necessary in this case to pollute "system".

> A thought that's only tangentially related: The multifile directive acknowledges that clauses are a meaningful unit of code reuse.  Unfortunately, multifile creates a single global predicate.  It might be helpful if modules could export their clauses to augment an existing, local predicate definition.

Not sure I follow. You can declare multifile predicates from your own module, besides system ones like "prolog", "user", or "system". This makes the predicates local to the module containing the primary multifile directive, even if the their clauses are spread (in another modules). Or maybe you mean something different when you write about global versus local?

Cheers,

Paulo


-----------------------------------------------------------------
Paulo Moura
Logtalk developer

Email: <mailto:[email protected]>
Web:   <http://logtalk.org/>
-----------------------------------------------------------------
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.