4
votes

The maplist/3 predicate has the following form

maplist(:Goal, ?List1, ?List2)

However the very similar function findall/3 has the form

findall(+Template, :Goal, -Bag)

Not only does it have a goal but a template as well. I've found this template to be quite useful in a number of places and began to wonder why maplist/3 doesn't have one.

Why doesn't maplist/3 have a template argument while findall/3 does? What is the salient difference between these predicates?

3
Suppose maplist/3 did have a template. How should that influence the semantics of this predicate?mat
@mat I'm not really sure what you are asking. If maplist/3 had a template it would likely have the form maplist(+Template1, +Template2, :Goal, ?List1, ?List2) like findall/3. I'm not sure what you mean by semantics here.Éamonn Olive
I mean: Could you for example please add a concrete example query where you show how you would use such a template in practice, or describe how it would influence answers?mat

3 Answers

5
votes

Templates as in findall/3, setof/3, and bagof/3 are an attempt to simulate proper quantifications with Prolog's variables. Most of the time (and here in all three cases) they involve explicit copying of those terms within the template.

For maplist/3 such mechanisms are not always necessary since the actual quantification is here about the lists' elements only. Commonly, no further modification happens. Instead of using templates, the first argument of maplist/3 is an incomplete goal that lacks two further arguments.

maplist(Goal_2, Xs, Ys).

If you insist, you can get exactly your template version using library(lambda):

templmaplist(Template1, Template2, Goal_0, Xs, Ys) :-
   maplist(\Template1^Template2^Goal_0, Xs, Ys).

(Note that I avoid calling this maplist/5, since this is already defined with another meaning)

In general, I rather avoid making "my own templates" since this leads so easily to misunderstandings (already between me and me): The arguments are not the pure relational arguments one is usually expecting. By using (\)/1 instead, the local variables are somewhat better handled and more visible as being special.

... ah, and there is another good reason to rather avoid templates: They actually force you to always take into account some less-than-truly-pure mechanism as copying. This means that your program may expose some anomalies w.r.t. monotonicity. You really have to look into the very details.

On the other hand without templates, as long as there is no copying involved, even your higher-order predicates will maintain monotonicity like a charm.

3
votes

Considering your concrete example will make clear why a template is not needed for maplist/3:

In maplist/N and other higher-order predicates, you can use currying to fix a particular argument.

For example, you can write the predicate:

p(Z, X, Y) :-
        Z #= X + Y.

And now your example works exactly as expected without the need for a template:

?- maplist(p(1), [1,2,3,4], [0,-1,-2,-3]).
true.

You can use library(lambda) to dynamically reorder arguments, to make this even more flexible.

2
votes

What is the salient difference between these predicates?

findall/3 (and family, setof/3 and bagof/3) cannot be implemented in pure Prolog (the monotonic subset without side effects), while maplist/N is simply a kind of 'macro', implementing boilerplate list(s) visit.

In maplist/N nothing is assumed about the determinacy of the predicate, since the execution flow is controlled by the list(s) pattern(s). findall/3 it's a list constructor, and it's essential the goal terminate, and (I see) a necessity to indicate what to retain of every succeeded goal invocation.