Re: term_expansion/2 with DCG clauses

Paulo Moura <[email protected]>
Newsgroups gmane.comp.ai.prolog.swi
Message-ID <[email protected]>
On 15/08/2013, at 19:51, Michael Hendricks <[email protected]> wrote:

> Hi Paulo,
> 
> On Thu, Aug 15, 2013 at 11:04 AM, Paulo Moura <[email protected]> wrote:
> 
>> 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.
>> 
> 
> Yes, "export macros by default" gives us a sad outcome.  Under recursive
> expansion, macros lose independence when we need to impose a specific
> expansion order.  Under first-solution expansion, we get the same problem
> under other conditions (like end_of_file in your chr example).

There are other Prolog extensions that also expand "end_of_file". It usually works as long the call to term_expansion/2 *fails* after doing their magic in order to give other extensions also expanding "end_of_file" a chance.

>>> 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?
>> 
> 
> Yeah, I didn't explain myself very well and my terminology was confusing.
> Let me try a different approach:
> 
> multifile predicates are kind of like an open house party.  Mom and Dad are
> out of town, so you buy some Kool-Aid and post on Facebook, "Party at my
> house this weekend"  You don't know who will show up, when they'll arrive
> or what they might leave in the fridge when they're done.  Sometimes we
> want predicates that are more like a country club dinner.  We hand out a
> few invitations to some quiet, respectable participants.  They arrive on
> time and clean up after themselves.
> 
> When the system module declares term_expansion/2 as multifile, the
> predicate is local to the system module but that module has no control over
> what clauses implement the predicate.  What if we had a directive called
> invite_only (to make something up) which allowed one to import clauses.
> For example,
> 
>    :- module(mine, []).
>    :- invite_only term_expansion/2.
>    % import term_expansion/2 clauses from modules a, b and c
>    :- use_module(a, [term_expansion/2]).
>    :- use_module(b, [term_expansion/2]).
>    :- use_module(c, [term_expansion/2]).
>    ...
> 
> At this point, mine:term_expansion/2 would contain clauses from all three
> modules.
> 
> multifile is "others push clauses into my predicate" and invite_only is "I
> pull clauses into my predicate".


At least a couple of ways of doing it in Logtalk. The key difference is that Logtalk's expansion mechanism doesn't declare either term_expansion/2 or goal_expansion/2 predicates as multifile:

https://github.com/LogtalkDotOrg/logtalk3/blob/master/core/expanding.lgt

The object pulling the contributions is free to select from each objects is accepting them. For example:

:- object(mine,
    implements(expanding)).

    % local clauses
    term_expansion(A, B) :-
        ...
    % contributed clauses
    term_expansion(A, B) :-
        a::term_expansion(A, B).
    term_expansion(A, B) :-
        b::term_expansion(A, B).
    term_expansion(A, B) :-
        c::term_expansion(A, B).

:- end_object.

An alternative solution (depending on the reuses granularity of the predicates you're encapsulating) is to have categories also implementing the "expanding" protocol and defining the object pulling the contributions as importing the sanctioned categories:

:- object(mine,
    implements(expanding),
    imports(a, b, c)).

    % local clauses
    term_expansion(A, B) :-
        ...
    % contributed clauses
    term_expansion(A, B) :-
        ^^term_expansion(A, B).

:- end_object.

Not a perfect solution, however, as the expansion predicates are declared dynamic by default, which can be seen as a backdoor. Wondering if I should make them static by default. I'm yet to find a practical case where asserting and retracting clauses for the expansion predicates is necessary.

Btw, multifile predicates in Logtalk require a primary declaration, composed by both a public/1 scope directive and a multifile/1 directive. You cannot simply declare a multifile predicate for another object to force it to take your clauses. But in Prolog modules systems such as the ones in SWI-Prolog or YAP (but not in SICStus Prolog) you can do:

----- a.pl -----
:- module(a, []).

:- multifile(b:p/1).
b:p(a).
--------------------

----- b.pl -----
:- module(b, []).

p(b).
--------------------

?- [a, b].
% a compiled into a 0.00 sec, 2 clauses
% b compiled into b 0.00 sec, 2 clauses
true.

?- b:p(X).
X = a ;
X = b.

The implicit flexibility illustrated by this example can be a handy feature but it can also be easily abused.

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.