When a new variable is created it can clash with existing variables (maybe not)

How can I fix this?

?- K = _A*_B*_C, L=_A*_B, M=_C, term_factorized( [K,L,M], X, Y).
K = _A*_B*M,
_C = M,
L = _A*_B,
X = [_A*M, _A, M],
Y = [_A=_A*_B].

It introduced a new variable _A which already exists. So it comes that the term Y = [_A=_A*_B] is wrong.

Thanks in Advance,
Frank Schwidom

It looks like _A and _A are actually different variables:

?- K = _A*_B*_C, L=_A*_B, M=_C, term_factorized( [K,L,M], X, Y), Y=[NEW_A=OLD_A*_].
K = OLD_A*_B*M,
_A = OLD_A,
_C = M,
L = OLD_A*_B,
X = [NEW_A*M, NEW_A, M],
Y = [NEW_A=OLD_A*_B].

So it should not make problems. But it is a little bit confusing.

Regards,
Frank.

This makes it easier:

?- TERM=(K = _A*_B*_C, L=_A*_B, M=_C, term_factorized( [K,L,M], X, Y)), TERM, numbervars(TERM).
TERM = (A*B*C=A*B*C, A*B=A*B, C=C, term_factorized([A*B*C, A*B, C], [D*C, D, C], [D=A*B])),
K = A*B*C,
_A = A,
_B = B,
_C = M, M = C,
L = A*B,
X = [D*C, D, C],
Y = [D=A*B].

Under SWI-Prolog version 8.2.4 this query produces

...
X = [_7176*M, _7176, M],
Y = [_7176=_A*_B].

I would call that a bug, since only _ is truly anonymous:

?- _ = 1, _ = 2.
true.

?- _A = 1, _A = 2.
false.

I am using version “SWI-Prolog version 9.2.2 for x86_64-linux”

Thanks. This is a bug. Pushed a fix. Your example is very complicated. This illustrates the issue:

v(f(X,X)).
?- v(_A).
_A = f(_A,_A).

The issues is triggered by two flags: toplevel_name_variables, which is by default true and causes the toplevel to name variables in the answer _A, _B, etc. rather than using _NNNN and toplevel_print_anon which is by default false (I set to true in my init.pl), which causes SWI-Prolog to not print the binding of variables that start with an underscore. If it prints _A, (default) it must check that the new variable is not already in use. That check was there, but broken.

I wonder whether we should enable hiding the bindings for “named anonymous” variables by default. I find it a very useful feature if I do not want some variable in a conjunction printed, typically because it is a huge term. So, you have q/1 that produces a long list and you only want the length. With toplevel_print_anon set to true, we can do

?- q(_List), length(_List, N).
N is 1000000.

SWISH does hide such variables.

3 Likes

It currently defaults to true in swi-prolog 9.2.4

Yes, changing the default to false, to hide e.g. _A, sounds good to me :grinning:

This idea is not bad. I didn’t know it was possible but it turns out to be very useful.

Regards, Frank

In case it helps, this is done this way in Ciao Prolog, and everyone seems to find it useful, so I certainly support it!

1 Like

With three votes and another system doing it this way, I pushed a change for the default :slight_smile: Thanks.

2 Likes

Should this also apply to variables with attributes? E.g.,

?- set_prolog_flag(toplevel_print_anon,false), freeze(_X,number(_X)).
freeze(_X, number(_X)).

I suppose that’s likely to be a bug in the person’s code, so hiding that _X deserves to be a separate option if anything, with the default being to show them.

Not sure why you say that, residual goals can be part of a valid answer (and very common when using clpBNR real arithmetic). It’s also likely that using a top level named variable starting with _ is usually motivated by the objective of not having them included in the printed answer.

So my question was whether the same convention for printing bound variables at the top level should be also be applied to unbound variables with attributes. I think it would be a nice feature addition.

I don’t know. I have the impression there are at least two types of attributed variables. Those that are intended to lead to some delayed execution to achieve a full answer (coroutining) and those that represent some valid answer in themselves (like an interval in your case).

Some people from the constraint world have advocated to enable the Prolog flag toplevel_residue_vars by default. This causes constraints to be printed even if they are not related to the answer. That is (I think) related to the first type: if we add a constraint that must be satisfied eventually to some variable and the variable is not related to the answer and the constraint was never resolved, the user should be made aware of this. I disagree making this the default because it requires wrapping toplevel goals in call_residue_vars/2, which causes significant overhead and disables GC for attributed variables.

This seems related. Possibly we should allow some annotation on the attribute? I do realise that the above distinction is not crystal clear though :frowning:

One thing you can do is to define attribute_goals//1 to return no goals for the second type of attributes and define attr_portray_hook/2 to print the attributed variable.

1 Like

I guess it depends on what you mean by “full answer”. In my interpretation an unbound variable in an answer means the proof is valid for any possible binding to that variable. In the freeze example I concocted, the interpretation would be any value which is a number. In this model all constraints are related to the answer, as are all unbound variables.

The top level makes choices as to what variables in the answer are output, conditional on the variable name. I was just suggesting the same conventions might be applied to attributed variables.

As @herme contributed, Ciao uses the same convention. I did a few more tests. It appears Ciao, SICStus, YAP and XSB all hide _A, at least by default. Ciao, SICStus and YAP print constraints on such variables. XSB hides them.

And Ciao has a little bug that is the opposite of the bug that initiated this discussion:

Ciao: Note hat the constraint is reported on an invented variable _B rather than on _A. There is no problem when using a normal variable.

?- freeze(_A, write(x)).
_B attributed '$frozen_goals'(_B,$:('write:write'(x))) ? 

Would be interesting whether this decisions are motivated or just accidental.