How to use include/3?

When trying to use include/3 to filter a list for elements verifying a given property, I always get an arity error for the first argument, independently of the arity.

?- include(between(2,3,X),[1,2,3,4],R).
ERROR: Undefined procedure: between/4
ERROR:   However, there are definitions for:
ERROR:         between/3

?- include(divmod(X,2,_,0),[1,2,3,4],R).
ERROR: Undefined procedure: divmod/5
ERROR:   However, there are definitions for:
ERROR:         divmod/4

include/3 provides the last parameter:

?- include(between(2,3), [1,2,3,4], R).
R = [2, 3].

Each call to between/3 needs a new 3rd argument, so you can’t provide it in the goal that’s used on each element of the list.

This might help you understand – call adds the last argument,

?- call(between(2,3), 2).
true.

?- between(2,3,Z).
Z = 2 ;
Z = 3.

?- call(between(2,3), Z).
Z = 2 ;
Z = 3.
2 Likes

Ah-ah, implicit argument and calling predicate with one less argument to make room for it.
Interesting and concise but not explained in the docs AFAICT.

Thanks a bunch for clarifying this for me :+1:

1 Like

Looks like a good topic to add to the Nice To Know category.

Of course it is difficult to judge how much explanation is useful (before it starts getting in the way), but I read, in the docs for include/3:

Filter elements for which Goal succeeds. True if List2 contains those elements Xi of List1 for which call(Goal, Xi) succeeds.

… and if at that point it is still not too obvious, you can find in the docs for call/2:

Append ExtraArg1, ExtraArg2, … to the argument list of Goal and call the result. For example, call(plus(1), 2, X) will call plus(1, 2, X) , binding X to 3.

If you think that the docs can be improved, I’d strongly suggest that you suggest an improvement. You can also add comments below the docs for individual predicates. One small issue with that is that you only see those comments if you open the documentation page for the predicate: you don’t see the comments and there is no way to know that they exist if you look at the docs at the “Section” level, for example here:

https://www.swi-prolog.org/pldoc/man?section=metacall

So here we see call/1, call/2, apply/2 and so on, but you don’t see that there are comments under call/2. You also can’t directly click on call/2, you’d have to click in the tree view on the left of the page. This has a very low (but measurable) constant annoyance factor to me :slight_smile:

2 Likes

It’s “obvious” if you’ve been exposed to currying, although it only looks like currying; under the covers, something else is going on.

One of the things that trips up people learning Prolog is that terms and predicates look the same – this is very convenient for things like “macros” (term_expansion and friends), but it can lead to confusion.

Whenever a predicate is defined, you can think of it as simultaneously defining some call facts that can look up a term and call the predicate that “looks like” the term. For example, defining

pred(A, B) :- ...

can be thought of as also defining

call(pred, A, B) :- pred(A, B).
call(pred(A), B) :- pred(A, B).
call(pred(A, B)) :- pred(A, B).

So, when you invoke include(pred(something), [a, list, of, values]), this ends up calling pred(something, a), pred(something, list), pred(something, of) , pred(something, values).

This gets more interesting with meta-predicates like maplist. For example, these are valid calls:

maplist(pred, [1,2,3,4], [20,30,40,50]). % pred(1,20), pred(2,30), pred(3,40), pred(4,50)
maplist(pred(1), [20,30,40,50]).  % pred(1,20), pred(1,30), pred(1,40), pred(1,50)