moduleprologmeta-predicate

What do the numeric arguments for meta_predicate mean in SWI-Prolog?


I am writing a Prolog program, and I am trying to incorporate modules into the program design to encapsulate complexity reduce redundant functionality.

One feature I am having difficulty with is the use of metapredicates. I would like to define a metapredicate in one module and then import it into a different module; this introduces complications. Fortunately, the meta_predicate directive helps resolve module prefixes, but I am having trouble understanding the arguments as described here: https://www.swi-prolog.org/pldoc/man?section=metapred

Specifically, I am having trouble with the numeric arguments. As per the documentation:

The argument is a term that is used to reference a predicate with N more arguments than the given argument term. For example: call(0) or maplist(1, +).

I understand that an argument denoted by the numeric value will be a term that is used to reference a predicate. What I do not understand is how the referenced predicate could have more arguments than the argument term. Can someone offer a more in-depth explanation of when the numeric argument is appropriate, or an example of when it would be appropriate to use it?


Solution

  • It is a formulation that is easy to understand only when one knows what it means, and should probably be redone.

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

    ... just means that the argument on the place of the "2" will be used as the kernel of a predicate that will be called. We have no special notation for that (a big mistake IMHO), so we will write it in standard fashion as a term like f(foo,bar), or f(foo), or f. What will the meta-predicate maplist/3 do with that term? Well, it will syntactically transform it and tack "2" additional arguments on its end (and only on its end, which results in awkwardness): f(foo,bar, ARG1,ARG2), or f(foo, ARG1,ARG2), or f(ARG1,ARG2). Then maplist/3 will call it.

    For example, for the aforementioned maplist/3, take this predicate taking two arguments:

    myprint(X,Y) 
       :- format("~w\n",[(X,Y)]).
    

    It can be used in a maplist/3 call like this, not indicating any args:

    maplist(myprint,[0,1,2,3],[a,b,c,d]).
    

    and two arguments, one from each list, will be tacked on the term myprint before that term is knighted to become a predicate, and called:

    ?- maplist(myprint,[0,1,2,3],[a,b,c,d]).
    0,a
    1,b
    2,c
    3,d
    true.
    

    This allows one to pass "partially filled-in calls". maplist/2 will tack 1 argument to the end of its first argument, so one can say:

    ?- maplist(myprint("foo+"),[a,b,c,d]).
    foo+,a
    foo+,b
    foo+,c
    foo+,d
    true.
    

    The above become actually usable in combination with Paulo Moura's library(yall), which wraps a goal into an anonymous predicate, exposing the arguments. Then one can flexibly rearrange things

    ?- maplist([X,Y]>>format("~w\n",[(X,Y)]),[0,1,2,3],[a,b,c,d]).
    0,a
    1,b
    2,c
    3,d
    true.
    
    ?- maplist([Y,X]>>format("~w\n",[(X,Y)]),[0,1,2,3],[a,b,c,d]).
    a,0
    b,1
    c,2
    d,3
    true.
    

    In fact library(yall) provides proper syntax for Lambda expressions that is cruelly missing from the ISO standard, to explicitly show the missing parameters.

    One could have imagined to have expressions like these long ago:

    ?- maplist(λX.verify(3,X), [1,2,3,4,5]).
    

    or to stay in ASCIIland, something like:

    ?- maplist(\X.verify(3,X), [1,2,3,4,5]).
    

    but it didn't happen.