The "empty atom" ... is it legitimate?

Trying:

set_prolog_flag(double_quotes,atom).

?- atom("").
true.

Why have an “empty atom”? Should an atom not be always nonempty?

On second thoughts, there seems to be a bijective mapping between atoms and strings and lists of characters, so having it makes sense.

On the other hand, I tried to use the empty atom as the empty functor, but that doesn’t work:

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

Note that the empty atom is '', not ””, whose interpretation depends on the double_quotes flag.

Yes, it works:

?- X = ''(1, 2).
X = ''(1, 2).

?- Empty_atom = "", X =.. [Empty_atom, 1, 2].
Empty_atom = '',
X = ''(1, 2).

Another experiment:

?- X =.. ["", 1, 2].
ERROR: Type error: `atom' expected, found `""' (a string)
ERROR: In:
ERROR:   [10] _7044=..["",1|...]
ERROR:    [9] <user>
?- set_prolog_flag(double_quotes, atom).
true.

?- X =.. ["", 1, 2].
X = ''(1, 2).

I am not in the mood for reading code at the moment, but my guess is that this is an oversight in regards to the treatment of double quotes during parsing.

PS: it seems that the name of the functor doesn’t need to be an atom, strictly – it can also be “atomic”.

?- atom([]).
false.

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

?- X = [](1, 2), X =.. [A,B,C].
X = [](1, 2),
A = [],
B = 1,
C = 2.

?- X = [](1, 2), X =.. [A,B,C], atom(A).
false.

I don’t know what to do with this knowledge :slight_smile:

1 Like

It is frustrating that the mysteries of Prolog implementations are not as fundamental or of general appeal as the mysteries of High Energy Physics.

Just to make sure, it still seems there is a small inconsistency. I don’t want to throw the “bug” word around without need, but still, the error message that you see is a bit fishy.

To summarize:

  • yes, empty atoms (strings, lists…) are fine;
  • Yes, you can use the empty atom as a name to a compound term;
  • Yes, you can set double_quotes to atom and then use =.. to make a compound term with an empty atom for a name;
  • No, you cannot just type ""(1, 2). Given everything else, this should also work, but apparently it doesn’t.
1 Like

Because Prolog’s grammar doesn’t allow ""(1,2) (or "abc"(1,2) for that matter). The grammar does allow double quotes in other contexts, such as X="abc"; the interpretation varies according to the double_quotes flag.

2 Likes

Good to know. I am still struggling to understand the need for "atoms in double quotes" (like, why?)

The settings for double_quotes and back_quotes give you full flexibility of representations. (Single quotes always get you atoms, and that’s baked into the grammar, which is why 'atom with spaces'(1,2) is just fine.)

Originally “…” meant a list of codes (that is: "abc" = [97, 98, 99]"). For convenience, SWI-Prolog has taken over the “…” notation for strings.
See also: https://www.swi-prolog.org/pldoc/man?section=ext-dquotes-motivation and https://www.swi-prolog.org/pldoc/man?section=strings

1 Like

Possibly. I kind of sympathize with the idea of @Boris and am tempted to call this a bug. On the other hand this seems totally artificial and not worth “fixing”.

I don’t think the ISO core standard says anything about the grammar allowing “abc”(1,2) or not. Since double_quotes flag diverting values are already implementation dependent,

so such a syntax would be possibly also implementation dependent. Currently my system allows “abc”(1,2) syntax. When double_quotes has an appropriate value. I just get this here:

Jekejeke Prolog 4, Runtime Library 1.4.2

?- set_prolog_flag(double_quotes, atom), 
    set_prolog_flag(single_quotes, codes).
Yes 
?- atom("abc").
Yes 
?- X = "abc"(1,2).
X = abc(1, 2)

Its a little annoying to realize this in the parser and unparser. But it makes things more consistent, and assures that read and write correspond to each other. Like here:

?- X = "ab c"(1,2).
X = "ab c"(1, 2)

Edit 20.03.2020:
It seems that SWI-Prolog follows a totally different strategy.
The flags do not really affect writing. I get:

SWI-Prolog (threaded, 64 bits, version 8.1.24)

?- set_prolog_flag(double_quotes, atom), 
        set_prolog_flag(single_quotes, codes).

?- X = "ab c".
X = 'ab c'.

?- (X = "ab c"), Y =.. [X,1,2].
X = 'ab c',
Y = 'ab c'(1, 2).
1 Like

Allowing "abc"(1,2) in the syntax would mean that a program could fail to compile based on a directive. There’s nothing inherently wrong with that – e.g., Python2 has from __future__ import print_function which determines whether print "abc" is legal or not (with the directive, only print("abc") is legal). [In the case of Python, this is easy to do – simply remove print from the lexical analyzer’s list of keywords.]

1 Like

Currently I have the flag local to the module or the pseudo module “user”. So when your program code is properly modularized, you can have mix and match. Means you can have different settings in different modules.

So programs cannot fail to compile just because of some side effect of some setting. In particular the top-level setting goes into the pseudo module “user”, and hence reconsulting programs that are modularized doesn’t impact them.

Here is a test, the file foo.pl:

:- module(foo, [test/1]).

:- set_prolog_flag(double_quotes, atom),
   set_prolog_flag(single_quotes, codes).

test("ab c").

Then in the top-level:

Jekejeke Prolog 4, Runtime Library 1.4.3

?- use_module('foo.pl').
% 1 consults and 0 unloads in 0 ms.
Yes

?- current_prolog_flag(double_quotes, X),
   current_prolog_flag(single_quotes, Y).
X = codes,   /* still the old values, wasn't changed by module consult */
Y = atom

?- test(X).
X = 'ab c'. /* display uses pseudo module "user" setting */

I think such an implementation specific extension of Prolog flags semantics is legit. At least the ISO core standard does not have modules, so what concerns modules is again implementation specific.

This was introduced a few years ago, when SWI started introducing strings or somesuch. Maybe this was SWI7, don’t remember exactly. So as to have SWI7 Prolog code and other code side by side.

Edit 20.03.2020:
The same works also in SWI-Prolog. Except that single_quotes is initially undefined, and I don’t know whether its even used by SWI-Prolog. I guess its not used since its also not restored:

SWI-Prolog (threaded, 64 bits, version 8.1.24)

?- current_prolog_flag(double_quotes, X).
X = string.

?- current_prolog_flag(single_quotes, X).
false.

?- use_module('foo.pl').
true.

?- current_prolog_flag(double_quotes, X).
X = string.

?- current_prolog_flag(single_quotes, X).
X = codes.

In furtherance of this, I have noticed:

% A term with the empty atom as functor

?- ?- F=..['','a','b','c','d'], write_canonical(F).
''(a,b,c,d)
F = ''(a, b, c, d).
% A term with the atom '[]' as functor

?- F=..['[]','a','b','c','d'], write_canonical(F).
'[]'(a,b,c,d)
F = '[]'(a, b, c, d).

But now:

% A term with the empty list as functor
?- F=..[[],'a','b','c','d'], F=..[H,A1,A2,A3,A4], 
    write_canonical(F), atomic(H), \+atom(H).
[](a,b,c,d)
F = [](a, b, c, d),
H = [],
A1 = a,
A2 = b,
A3 = c,
A4 = d.

That’s probably a leftover from when the empty list used to be an atom.

Nope:

16 ?- writeq([](a,b)).
[](a,b)
true.

17 ?- writeq('[]'(a,b)).
'[]'(a,b)
true.

18 ?- [](a,b) == '[]'(a,b).
false.

[] is a blob and these are allowed as functor name. They started of as an abstraction of atoms, turning normal atoms into a subtype. Whether you want to do this is another issue. Not sure there is anything meaningful about this code:

19 ?- open_null_stream(X), T =.. [X,a].
X = <stream>(0x55d87c5af790),
T = <stream>(0x55d87c5af790)(a).

Using [] as a functor name is not that uncommon though and there was no reason to make this illegal.

2 Likes