Some clarifications for "mode indicators" needed

I’m going through the list of “mode indicators” again:

And created a little cheatsheet:

… and have a few question, which should be easy to answer (cough):


Currently the text (which I remember to have partially written myself, so any errors extant are mine) says: “partial term (a term which may or may not be ground)”.

Is this true? Are all of _, [a|X] and [a,b] considered partial terms?


I never got that I am not supposed to pass an unbound variable at parameter positions marked with a + (that’s so, right?)

But I can pass open lists, still?

For example, for foldl(:Goal, +List, +V0, -V), an unbound variable at List position is a not supported (although it does work)

X = [],
Final = start ;
X = [_6450],
Final = x ;
X = [_6450, _11086],
Final = x 

but an open list is still ok? It is a special kind of nonground term (and so is an open tree or open graph) and the empty open list is just an unbound variable and indistinguishable from any other domain. So maybe + also means that open lists are disallowed, too, to keep the domains apart?

X = [],
Final = x ;
X = [_8596],
Final = x ;
X = [_8596, _9992],
Final = x ;
X = [_8596, _9992, _11388],
Final = x 

Here is another thought:

foldl(:Goal, +List, +V0, -V)

… should not have any opinion on the mode of V0 and V because the mode of these depends on Goal. They could be anything (this is where the type expression in Haskell gets complex I imagine?)


Somewhat related, the mode indicators for is_list/2 is


I do think this should be is_list(@Term)?

1 Like

Disclaimer: the usual disclaimers apply.

Could it be that you are overthinking this? Remember that the mode indicators, in SWI-Prolog, are only for documentation purposes, with all the ambiguity and edge cases that follow.

A1: when you talk about argument instantiation, a partially bound term is not fully instantiated because it has free variables in it. This would mean that [a,b] is not partially instantiated; it is fully instantiated.

A2: The plus signifies that the predicate is meant to be used with an argument that has some type or shape, usually. This is because that shape of the input argument is part of the algorithm that the predicate implements.

The foldl example is interesting, because it exposes one of the many weaknesses of the mode indicators (margin note: those weaknesses are probably somewhat understood, since the mode indicators are only used to communicate intent from one programmer to another…).

In the foldl case, the implementation of Goal in the first argument might take the shape of any of its arguments and use that to supply shape to any other argument. One demonstration is this example of converting a list of goals to a conjunction from the material hiding in the “see also” at the top of the docs for library(apply).

flip_conj(Goal, (Goal,Conj), Conj).

?- foldl(flip_conj, [p,q,r], Conj, true).
=> Conj = (p, q, r, true).

(this is somewhere in the middle there)

So basically the only shape in the Goal is in the second argument, and in the foldl invocation the third argument is actually the “output”. But this is fine.

A3: It seems this should indeed be changed to is_list(@Term). I wonder if the + there isn’t just an artifact of an earlier implementation of is_list/1.

This topic is very closely related to how you talk about determinism in the docs. I don’t think the language in the docs strictly follows something like RFC 2119, and again, this is fine. When I read MUST, I understand “When I implemented this predicate, I did not put unnecessary effort in figuring out what might happen if you do otherwise”. And this is also fine. For example, partition/5 says “Pred must be deterministic”, but you can ignore this MUST and still get what you asked for:

any_place(_, <).
any_place(_, =).
any_place(_, >).

?- partition(any_place, [x], Less, Equal, Greater).
Less = [x],
Equal = Greater, Greater = [] ;
Less = Greater, Greater = [],
Equal = [x] ;
Less = Equal, Equal = [],
Greater = [x].