prologmeta-predicate

How to specify a predicate as parameter to another in prolog?


I want to write a prolog predicate that holds if the predicate appearing as first parameter holds for all elements of the list appering in second parameter. Here is something that I have tried:

?- listing(all).
all(pred(_), [A|B]) :-
    pred(A),
    all(pred(_), B).
all(pred(_), [x]) :-
    pred(x).

Something like the following should return true. Is this possible in Prolog?

all(number, [3, 5 ,7]).

Solution

  • You can use call/n [swi-doc]:

    call(X, Y).
    

    Given X=number, and Y is for example 3, it will call number(3). In case X is a term, like number(1), it calls it like number(1, 3), as if the predicate was "curried".

    So you can implement your function as:

    all(_, []).
    all(P, [A|B]) :-
        call(P, A),
        all(P, B).

    although for more efficient backtracking, it might be better to swap the parameters:

    all(P, L) :-
        all2(L, P).
    
    all2([], _).
    all2([A|B], P) :-
        call(P, A),
        all2(B, P).

    But the predicate you here aim to implement already exists in some popular Prolog interpreters maplist/2 [swi-doc].

    This will call the Goal on all elements of the list, or fail if the predicate fails at some point.

    You can also construct functors with (=..)/2 [swi-doc]. For example:

    X =.. [number, 1, 3]
    

    will result in X = number(1, 3). You can then use call call/1 [swi-doc] with the constructed functor to call the functor as if it was a predicate, like:

    X =.. [number, 1, 3], call(X).
    

    Note that the (=..)/2 predicate does not work with this "currying" of functors, for example:

    ?- X =.. [number(1), 3].
    ERROR: Type error: `atom' expected, found `number(1)' (a compound)
    ERROR: In:
    ERROR:    [8] _6428=..[number(1),3]
    ERROR:    [7] <user>
    

    .