Surprising result with equals dot dot

Using SWI-Prolog version 8.5.7

?- X = (2, 3), X =.. Y, length(Y, YLen), nth1(1, Y, Y1).
X =  (2,3),
Y = [,,2,3],
YLen = 3,
Y1 =  (,).

Is it correct for Y1 to ever be a tuple? Why default to such a confusing display of Y, which looks like a list of 4 elements?

1 Like

Maybe this explains things - with write_canonical(Y1) (using format/3, to get it into a Y1str as a string):

?- X = (2, 3), X =.. Y, length(Y, YLen), nth1(1, Y, Y1), format(string(Xstr), '~k', [X]), format(string(Y1str), '~k', [Y1]).
X =  (2, 3),
Y = [',', 2, 3],
YLen = 3,
Y1 =  (','),
Xstr = "','(2,3)",
Y1str = "','".

I’m also using 8.5.7, so it’s curious that your output is different.

Aah, this is what unintentionally adjusted my write settings to remove the single quotes (was in my ~/.config/swi-prolog/ ) :

:- set_prolog_flag(answer_write_options,[max_depth(100)]).

I was expecting that, since I was not changing the other defaults, that they would keep their usual, sensible values :grinning:
I have changed it to:

:- set_prolog_flag(answer_write_options, [quoted(true), portray(true), max_depth(100), attributes(portray)]).

Does Y1, as a comma, make sense?

Yes. In this context, comma is an operator:

?- X =  (a, b).
X =  (a, b).

?- current_op(Precedence, Type, ',').
Precedence = 1000,
Type = xfy.

I meant, why does Y1 involve a comma, when presumably it should be blank, since I specified (2, 3) instead of term_name_here(2, 3).

As opposed to the perfectly normal question and answer of:

?- X = normal_term(2, 3), X =.. Y, length(Y, YLen), nth1(1, Y, Y1).
X = normal_term(2,3),
Y = [normal_term,2,3],
YLen = 3,
Y1 = normal_term.

As you can see, there is no distinction between a term that’s specified with an operator and one that’s given in “prefix” form:

?- (a,b) = ','(a,b)

Perhaps this is clearer:

?- (a+b) = '+'(a,b).

The only place where comma isn’t an operator is for separating the arguments in a term; everywhere else, it’s an operator. Compare:

?- write_canonical(p(a,b)).

?- write_canonical(p((a,b))).

?- write_canonical(p((a,b),c)).

?- write_canonical(p((a,b),(c,d))).
1 Like

I guess your question got answered already? The bottom line is, this:

(2, 3)

is not an “anonymous tuple” or anything of the sort. It is not the same as foo(2, 3) with just the “foo” missing.

foo(2, 3)

is a compound term with name “foo”, arity 2, and the two arguments are 2, 3.

(2, 3)

happens to parse as a compound term with name “,”, arity 2, and arguments 2, 3, because , is an operator. This is why this succeeds:

?- (2, 3) = ','(2, 3).

Note that here the “','” is indeed an atom of length one.

This fails:

?- (2, 3) = ,(2, 3).
ERROR: Syntax error: Operator expected
ERROR: (2, 3) = 
ERROR: ** here **
ERROR: ,(2, 3) . 

Why does it fail and why do you think that it is “not allowed to redefine the comma”?

PS: I suspect or maybe faintly remember that with a different parser there might be a Prolog that allows you to redefine the comma.

1 Like

There is a so-called “comma list”, but its use is frowned upon as bad style; and a “regular” list is easier. Here’s an example (it works because the “,” operator is right-associative:
(a,b,c) = ','(a,','(b,c)):

% don't do this
sum_comma_list((X,Xs), Sum) :- !, % this cut is needed
    sum_comma_list(Xs, Sum0),
    Sum is X + Sum0.
sum_comma_list(X, X). % X \= (_,_)

?- sum_list([1,2,3], Sum).
Sum = 6.

Here is better style (notice that it doesn’t need a cut and the clauses can be in any order and it also allows a 0-length list, whereas a “comma list” must have at least one item):

sum_list([X|Xs], Sum) :-
    sum_list(Xs, Sum0),
    Sum is X + Sum0.
sum_list([], 0).

?- sum_comma_list((1,2,3), Sum).
Sum = 6.

My favorite demonstration about the pitfalls of comma lists (as seen somewhere on the internets):

?- [A, B] = [A, B, C].
false. % of course

?- (A, B) = (A, B, C).
B =  (B, C). % WAT?

Coming from a language that has “tuples” this second query should come as a nasty surprise. It also shows why comma lists are not tuples in the widely accepted meaning of the word “tuple”.

And of course [] (the empty list) is a thing while () (the empty tuple?) is not a thing, as you mention.

Both Python (hugely popular) and Haskell (hugely popular in some circles) have tuples that look like (a, b); and Prolog accepts this construct, and it seems to work. The end result is that you see a lot of Prolog code that uses those, some of it apparently coming from people teaching Prolog to other people, within the safe space of educational institutions.


Thank you, you are correct. However, no one who wants to use “tuples in Prolog” would start adding true as the last element of all their tuples, I thought. Maybe I am wrong.

Sometimes, when i have a goal/predicate as so pred(A, B) :- and i need to add an argument without adding causing pred/2 to become pred/3, I pass something like (X, Y) to bind with, say, B.

When placed before the original pred/2, these get found first.

Usually, its to try out things and to later rework the code without such contraption …

But, its a quick way to add an argument …

Do not do that. Use a pair, X-Y or just name your “tuple” somehow:

pred(A, B) -----> pred(A, X-Y)

or maybe

pred(A, B) -----> pred(A, x(X, Y))

I would call comma lists a “code smell” but the word “smell” is too nice :wink:

Yes, I use v(a, b, c) with the mnemonic “vector” or maybe “values”, if it is really so generic that I cannot come up with a short word that describes my a, b, c any better.

AFAIK, O’Keefe’s “The Craft of Prolog” already claims “never use a list for something of a fixed number of elements”. Instead, use a tuple as a compound term and try to give it a sensible name. For anonymous tuples with two arguments, use A-B which works nicely with library(pairs), keysort/2 and similar built-ins and libraries. I also tend to use v(…) if I see no need for a sensible name :slight_smile:


The form A-B tends to be used with the implied semantics Key-Value, and that can lead to subtle bugs if you assume the Keys are unique (I just fixed some code that made such a mistake), even though keysort/2 explicitly makes no assumption about uniqueness of keys.
I’ve also seen A-B used for difference lists (and sometimes A\B), with the mental image of subtracting B from the end of A.

As others have said, best to use a tuple; v(A,B,C) is better than A-B-C (and also slightly more efficient), and even better if you can use something more meaningful than “v” for the functor name.

The “minus” as in A-B is only good for a pair, it doesn’t extend to A-B-C. That suffers the exact same problem as “comma lists”, BTW, just swapped:

?- A-B = A-B-C.
A = A-C,
B = C.

The “clean” way to extend - from a pair to a triple, quadriple etc would be to do the exact same thing you’d do with the comma:

','(a, b), ','(a, b, c), ','(a, b, c, d)
-(a, b), -(a, b, c), -(a, b, c, d)

what I now learned and find curious is that others also use v(a, b, ...) for this.

There is a lot of interesting lessons to be learned from R. IIRC the c() in R is a function that combines or maybe concatenates its arguments to a one-dimensional vector (and it takes as many arguments as you give it…).

It seems that ECLiPSe has a very similar interface to arrays as R. In R at least you can at run time change the dimensions of an array (efficiently) by assigning a vector of dimensions to your array.