4

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?

Wheat Wizard
  • 3,982
  • 14
  • 34
  • 3
    Suppose `maplist/3` *did* have a template. How should that influence the semantics of this predicate? – mat Jan 03 '18 at 17:21
  • @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. – Wheat Wizard Jan 03 '18 at 17:26
  • 1
    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 Jan 03 '18 at 17:28

3 Answers3

5

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.

false
  • 10,264
  • 13
  • 101
  • 209
3

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.

mat
  • 40,498
  • 3
  • 51
  • 78
  • Why is a template used for `findall` then? I can see why neither *needs* a template, I'm just interested in knowing why they are different. – Wheat Wizard Jan 03 '18 at 17:44
  • 1
    `maplist/3` has a very specific meaning, where `call/N` is used with corresponding pairs of list elements as the last two arguments. But `findall/3` allows *any* goal in its argument and calls it without any additional restrictions or assumptions. How would `findall/3` know, in general, which arguments we are interested in? Thus, they must be specified for `findall/3`, but need not be specified for `maplist/N`, where the convention is fixed. – mat Jan 03 '18 at 17:47
2

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.

CapelliC
  • 59,646
  • 5
  • 47
  • 90