prologmeta-predicatelogical-purity

Goal expansion for an `if_/3` operator in Prolog


I'm writing a tokeniser and I want to use if_/3 to preserve in my code.

The code looks like the following code1 on the left—but I want it to look like the one on the right.

if_(Cond1_1,                       %     (   Cond1_1
    Then1,                         %     *=> Then1
    if_(Cond2_1,                   %     ;   Cond2_1
        Then2,                     %     *=> Then2
        if_(Cond3_1,               %     ;   Cond3_1
            Then3,                 %     *=> Then3
            if_(Cond4_1,           %     ;   Cond4_1
                Then4,             %     *=> Then4
                if_(Cond5_1,       %     ;   Cond5_1
                    Then5,         %     *=> Then5
                    Else5          %     ;   Else5
   )   )   )   )   ).              %     ).

To do the rewriting of (*=>)/2 to if_/3 in SWI-Prolog I came up with:

:- op(1050,xfy,*=>).

:- multifile goal_expansion/2.
goal_expansion((Cond *=> Then ; Else), if_(Cond,Then,Else)).
goal_expansion( Cond *=> Then        , (call(Cond,true), call(Then))).

"Done," I thought...

But I became doubtful after reading the SWI-Prolog documentation for goal_expansion/2:

Only goals appearing in the body of clauses when reading a source file are expanded using this mechanism, and only if they appear literally in the clause, or as an argument to a defined meta-predicate that is annotated using `0' (see meta_predicate/1). Other cases need a real predicate definition.

So here's my actual question: Do I need a real predicate definition, too?


Footnote 1: The actual code has an even longer chain of else ifs.


Solution

  • You need a if_/3 predicate definition, to at least assign it a meta predicate declaration, otherwise expansion will stop, if there is no meta predicate declaration for if_/3 itself.

    You can try yourself, I am using only this expansion:

    :- op(1050,xfy,*=>).
    :- multifile goal_expansion/2.
    goal_expansion((Cond *=> Then ; Else), if_(Cond,Then,Else)).
    

    Without meta predicate declaration:

    Welcome to SWI-Prolog (threaded, 64 bits, version 8.1.4)
    
    ?- expand_goal((a *=> b; c *=> d; e), X).
    X = if_(a, b,  (c*=>d;e)).
    

    With meta predicate declaration:

    Welcome to SWI-Prolog (threaded, 64 bits, version 8.1.4)
    
    :- meta_predicate if_(1,0,0).
    ?- expand_goal((a *=> b; c *=> d; e), X).
    X = if_(a, b, if_(c, d, e)).
    

    This is same behaviour in SWI-Prolog and Jekejeke Prolog. You could study the source code to better understand why the meta predicate declaration is needed.

    See for example here:
    https://github.com/jburse/jekejeke-devel/blob/master/jekrun/headless/jekpro/frequent/standard/expand.p#L220