As of version 7, SWI-Prolog lists can be distinguished 
unambiguously at runtime from ./2'[]'. The constant [] is special 
constant that is not an atom. It has the following properties:
?- atom([]). false. ?- atomic([]). true. ?- [] == '[]'. false. ?- [] == []. true.
The `cons' operator for creating list cells has changed from the 
pretty atom '.' to the ugly atom '[|]', so we 
can use the
'.' for other purposes. See section 
5.4.1.
This modification has minimal impact on typical Prolog code. It does 
affect foreign code (see section 
11) that uses the normal atom and compound term interface for 
manipulation lists. In most cases this can be avoided by using the 
dedicated list functions. For convenience, the macros ATOM_nil 
and ATOM_dot are provided by
SWI-Prolog.h.
Another place that is affected is write_canonical/1. 
Impact is minimized by using the list syntax for lists. The predicates read_term/2 
and
write_term/2 
support the option dotlists(true), which causes
read_term/2 
to read .(a,[]) as [a] and write_term/2 
to write [a] as .(a,[]).
[|]' and [] for listsRepresenting lists the conventional way using ./2
./2a.B cannot be 
distinguished from [a|B]. Freeing ./2
'[]' as list terminator prevents dynamic 
distinction between atoms and lists. As a result, we cannot use type 
polymorphism that involve both atoms and lists. For example, we cannot 
use
multi lists (arbitrary deeply nested lists) of atoms. Multi 
lists of atoms are in some situations a good representation of a flat 
list that is assembled from sub sequences. The alternative, using 
difference lists or DCGs is often less natural and sometimes demands for 
`opening' proper lists (i.e., copying the list while replacing the 
terminating empty list with a variable) that have to be added to the 
sequence. The ambiguity of atom and list is particularly painful when 
mapping external data representations that do not suffer from this 
ambiguity.
At the same time, avoiding '[]' as a list terminator 
makes the various text representations unambiguous, which allows us to 
write predicates that require a textual argument to accept both atoms, 
strings, and lists of character codes or one-character atoms. 
Traditionally, the empty list can be interpreted both as the string "[]" 
and "".