Reporting as singleton variables all variables that start with an underscore

66 views
Skip to first unread message

Paulo Moura

unread,
Jun 25, 2018, 6:41:57 AM6/25/18
to SWI-Prolog
Hi,

I'm wondering if there's some way of getting the read_term/3 predicate to report as singleton variables all variables that start with an underscore (minus, of course, the anonymous variable). For example:

?- read_term(user_input, T, [singletons(L)]).
|: a(_X, _Y, _x, _y).

T = a(_3612, _3614, _3616, _3618),
L = ['_x'=_3616, '_y'=_3618].

In my particular case, I ideally would get:

| ?- read_term(user_input, T, [singletons(L)]).
|: a(_X, _Y, _x, _y).
T = a(_A,_B,_C,_D),
L = ['_X'=_A,'_Y'=_B,'_x'=_C,'_y'=_D] ? yes

The idea of using variables such as _X is a common practice and helps to understand code compared with the alternative of just using anonymous variables. But unfortunately the interpretation of variables that start with an underscore followed by an upper case letter depends on the Prolog system. Is there any acceptable hack or hidden setting to get the behavior illustrated by the second query in SWI-Prolog?

Thanks,
Paulo

Jan Wielemaker

unread,
Jun 25, 2018, 7:27:54 AM6/25/18
to Paulo Moura, SWI-Prolog
Hi Paulo,
Get all variable names and do your own logic?

?- read_term(X, [variable_names(L)]).
|: a(_X, _Y, _x, _y).

X = a(_9354, _9356, _9358, _9360),
L = ['_X'=_9354, '_Y'=_9356, '_x'=_9358, '_y'=_9360].

As the variable_names option is fairly portable (I hope; you'll know
better), this should be more portable anyway.

Note that the current logic is the result of some debates supporting
languages that do not have the notion of upper and lowercase. The
current scheme was suggested by Richard O'Keefe AFAIK.

Cheers --- Jan

>
> Thanks, Paulo
>

Paulo Moura

unread,
Jun 25, 2018, 7:53:01 AM6/25/18
to SWI-Prolog
Hi Jan,

> On 25 Jun 2018, at 12:27, Jan Wielemaker <j...@swi-prolog.org> wrote:
>
> Hi Paulo,
>
> On 06/25/2018 12:41 PM, Paulo Moura wrote:
>> Hi,
>> I'm wondering if there's some way of getting the read_term/3
>> predicate to report as singleton variables all variables that start
>> with an underscore (minus, of course, the anonymous variable). For
>> example:
>> ?- read_term(user_input, T, [singletons(L)]). |: a(_X, _Y, _x, _y).
>> T = a(_3612, _3614, _3616, _3618), L = ['_x'=_3616, '_y'=_3618].
>> In my particular case, I ideally would get:
>> | ?- read_term(user_input, T, [singletons(L)]). |: a(_X, _Y, _x,
>> _y). T = a(_A,_B,_C,_D), L = ['_X'=_A,'_Y'=_B,'_x'=_C,'_y'=_D] ? yes
>> The idea of using variables such as _X is a common practice and helps
>> to understand code compared with the alternative of just using
>> anonymous variables. But unfortunately the interpretation of
>> variables that start with an underscore followed by an upper case
>> letter depends on the Prolog system. Is there any acceptable hack or
>> hidden setting to get the behavior illustrated by the second query in
>> SWI-Prolog?

Thanks for the quick reply.

This is a recent issue due to the introducing of parameter variables in Logtalk 2.14.0, which use the syntax _VariableName_. In the case of SWI-Prolog and others that don't interpret _Var as a singleton, typos in parameter variables (that turn them singletons) are currently not detected (unless the programmer uses _varName_).

> Get all variable names and do your own logic?

I did notice the term_singletons/2 built-in predicate in the documentation, which could be used to filter the list returned by the variable_names/2 option. But that requires costly list operations and that would result in a too big overhead when compiling files :(

> ?- read_term(X, [variable_names(L)]).
> |: a(_X, _Y, _x, _y).
>
> X = a(_9354, _9356, _9358, _9360),
> L = ['_X'=_9354, '_Y'=_9356, '_x'=_9358, '_y'=_9360].
>
> As the variable_names option is fairly portable (I hope; you'll know
> better), this should be more portable anyway.

My question did indeed resulted from a nasty portability issue. CxProlog, ECLiPSe, GNU Prolog, JIProlog, and SICStus Prolog (and a few more) report variables such as _X as a singleton while others like SWI-Prolog consider it an anonymous variable (sometimes I call it a "named anonymous variable" :-), which is quite useful for self-documenting code. In the former case, the overhead is smaller as I just need to filter any singletons (if any) that start with an underscore followed by an upper case letter for consistent semantics.

> Note that the current logic is the result of some debates supporting
> languages that do not have the notion of upper and lowercase. The
> current scheme was suggested by Richard O'Keefe AFAIK.

The idea is quite old indeed but I don't recall the historic bits around it.

Cheers,
Paulo

Jan Wielemaker

unread,
Jun 25, 2018, 8:00:07 AM6/25/18
to Paulo Moura, SWI-Prolog
On 06/25/2018 01:52 PM, Paulo Moura wrote:
> Hi Jan,
>
>> On 25 Jun 2018, at 12:27, Jan Wielemaker <j...@swi-prolog.org> wrote:
>>
>> Hi Paulo,
>>
>> On 06/25/2018 12:41 PM, Paulo Moura wrote:
>>> Hi,
>>> I'm wondering if there's some way of getting the read_term/3
>>> predicate to report as singleton variables all variables that start
>>> with an underscore (minus, of course, the anonymous variable). For
>>> example:
>>> ?- read_term(user_input, T, [singletons(L)]). |: a(_X, _Y, _x, _y).
>>> T = a(_3612, _3614, _3616, _3618), L = ['_x'=_3616, '_y'=_3618].
>>> In my particular case, I ideally would get:
>>> | ?- read_term(user_input, T, [singletons(L)]). |: a(_X, _Y, _x,
>>> _y). T = a(_A,_B,_C,_D), L = ['_X'=_A,'_Y'=_B,'_x'=_C,'_y'=_D] ? yes
>>> The idea of using variables such as _X is a common practice and helps
>>> to understand code compared with the alternative of just using
>>> anonymous variables. But unfortunately the interpretation of
>>> variables that start with an underscore followed by an upper case
>>> letter depends on the Prolog system. Is there any acceptable hack or
>>> hidden setting to get the behavior illustrated by the second query in
>>> SWI-Prolog?
>
> Thanks for the quick reply.
>
> This is a recent issue due to the introducing of parameter variables in Logtalk 2.14.0, which use the syntax _VariableName_. In the case of SWI-Prolog and others that don't interpret _Var as a singleton, typos in parameter variables (that turn them singletons) are currently not detected (unless the programmer uses _varName_).

Do you have an example?

>
>> Get all variable names and do your own logic?
>
> I did notice the term_singletons/2 built-in predicate in the documentation, which could be used to filter the list returned by the variable_names/2 option. But that requires costly list operations and that would result in a too big overhead when compiling files :(
>
>> ?- read_term(X, [variable_names(L)]).
>> |: a(_X, _Y, _x, _y).
>>
>> X = a(_9354, _9356, _9358, _9360),
>> L = ['_X'=_9354, '_Y'=_9356, '_x'=_9358, '_y'=_9360].
>>
>> As the variable_names option is fairly portable (I hope; you'll know
>> better), this should be more portable anyway.
>
> My question did indeed resulted from a nasty portability issue. CxProlog, ECLiPSe, GNU Prolog, JIProlog, and SICStus Prolog (and a few more) report variables such as _X as a singleton while others like SWI-Prolog consider it an anonymous variable (sometimes I call it a "named anonymous variable" :-), which is quite useful for self-documenting code. In the former case, the overhead is smaller as I just need to filter any singletons (if any) that start with an underscore followed by an upper case letter for consistent semantics.

I thought using _Name was more or less de-facto standard. It surely has
been like this in SWI-Prolog very early in its development and I don't
think I invented it :)

Cheers --- Jan

Paulo Moura

unread,
Jun 25, 2018, 9:20:16 AM6/25/18
to SWI-Prolog
Hi,
:- object(foo(_X_, _Y_)).

:- public(bar/1).
bar(Z) :- Z is _X_ + _T_.

:- end_object.

In this case _T_ is a singleton and should be reported. The current workaround is to use_varName_ instead. Rewriting the example as:

:- object(foo(_x_, _y_)).

:- public(bar/1).
bar(Z) :- Z is _x_ + _t_.

:- end_object.

Then, running on SWI-Prolog, _t_ would be reported as a singleton.


>>> Get all variable names and do your own logic?
>> I did notice the term_singletons/2 built-in predicate in the documentation, which could be used to filter the list returned by the variable_names/2 option. But that requires costly list operations and that would result in a too big overhead when compiling files :(
>>> ?- read_term(X, [variable_names(L)]).
>>> |: a(_X, _Y, _x, _y).
>>>
>>> X = a(_9354, _9356, _9358, _9360),
>>> L = ['_X'=_9354, '_Y'=_9356, '_x'=_9358, '_y'=_9360].
>>>
>>> As the variable_names option is fairly portable (I hope; you'll know
>>> better), this should be more portable anyway.
>> My question did indeed resulted from a nasty portability issue. CxProlog, ECLiPSe, GNU Prolog, JIProlog, and SICStus Prolog (and a few more) report variables such as _X as a singleton while others like SWI-Prolog consider it an anonymous variable (sometimes I call it a "named anonymous variable" :-), which is quite useful for self-documenting code. In the former case, the overhead is smaller as I just need to filter any singletons (if any) that start with an underscore followed by an upper case letter for consistent semantics.
>
> I thought using _Name was more or less de-facto standard. It surely has been like this in SWI-Prolog very early in its development and I don't
> think I invented it :)

The issue seems to be that of a missing separation between what the read_term/3 predicate returns and what's reported as singletons by the compiler. In CxProlog, ECLiPSe, GNU Prolog, JIProlog, and SICStus Prolog, a source file containing the single fact:

a(_X, _Y, _x, _y).

compiles without any singleton warnings. But in all these systems the read_term/3 predicate correctly (as per the ISO standard specification) returns all the variables as singletons. Using _Name is a de facto standard but that doesn't require a non-compliant singletons/1 option for the read_term/3 predicate. Instead, it requires a post-processing of the singletons list by the compiler when reading a source file term to decide which singletons should be reported to the user. Would this be a complicated fix for SWI-Prolog? Could it cause any significant backward compatibility issues?

Cheers,
Paulo

Jan Wielemaker

unread,
Jun 25, 2018, 1:39:17 PM6/25/18
to Paulo Moura, SWI-Prolog
On 25/06/18 15:20, Paulo Moura wrote:
> The issue seems to be that of a missing separation between what the
> read_term/3 predicate returns and what's reported as singletons by
> the compiler. In CxProlog, ECLiPSe, GNU Prolog, JIProlog, and SICStus
> Prolog, a source file containing the single fact:
>
> a(_X, _Y, _x, _y).
>
> compiles without any singleton warnings. But in all these systems the
> read_term/3 predicate correctly (as per the ISO standard
> specification) returns all the variables as singletons. Using _Name
> is a de facto standard but that doesn't require a non-compliant
> singletons/1 option for the read_term/3 predicate. Instead, it
> requires a post-processing of the singletons list by the compiler
> when reading a source file term to decide which singletons should be
> reported to the user. Would this be a complicated fix for SWI-Prolog?
> Could it cause any significant backward compatibility issues?

Pushed a change along these lines. I'm not really happy with it. The
notion of singleton should probably never have been in read_term/2,3.
True, there is a clear notion of singletons: any variable except _ that
appears only once in a term. Logically that isn't very interesting
though, and term_singletons/2 is logically a more interesting notion.
One could argue for each appearance of '_' to appear in the singleton
list, e.g. a(_,_) returns ['_'=_0, '_'=_1] as singletons.

Syntactical singletons, are not very useful as a basis for error warning
as we actually want to know whether a named variable communicates a term
between two members of a conjunction.

Anyway, as it is the option now retuns the clearly defined (but IMO
rather useless) option of syntactic singletons and warnings use what
they always did (which isn't perfect either).

I wouldn't think this affects anything but the development envronment.

Let me know if this fixes your problems.

Cheers --- Jan

Paulo Moura

unread,
Jun 25, 2018, 2:12:22 PM6/25/18
to SWI-Prolog
Hi Jan,

Thanks for the quick fix to the read_term/3 predicate singletons/1 option, which now is ISO compliant :-) Compiled and tested. Works nicely and solves the issue I mentioned :-)

Cheers,
Paulo

P.S. Logtalk users using the updated SWI-Prolog git version will need to update to the current Logtalk git version to benefit. The fix is detected using an if/1 conditional compilation directive that calls the handy SWI-Prolog read_term_from_atom/3 predicate in order to set the Logtalk "underscore_variables" flag correctly for any supported version of SWI-Prolog.
Reply all
Reply to author
Forward
0 new messages