Bug: (list? (cons 4 '(1 2 3))) returns false!

27 views
Skip to first unread message

Handkea fumosa

unread,
Jun 28, 2009, 12:07:24 AM6/28/09
to Clojure
user=> (list? '(1 2 3))
true
user=> (list? (cons 4 '(1 2 3)))
false

Stephen C. Gilardi

unread,
Jun 28, 2009, 12:41:20 AM6/28/09
to clo...@googlegroups.com

user=> (doc cons)
-------------------------
clojure.core/cons
([x seq])
Returns a new seq where x is the first element and seq is
the rest.
nil
user=> (cons 4 '(1 2 3))
(4 1 2 3)
user=> (seq? (cons 4 '(1 2 3)))
true
user=> (first (cons 4 '(1 2 3)))
4
user=> (rest (cons 4 '(1 2 3)))
(1 2 3)
user=> (list? (rest (cons 4 '(1 2 3))))
true
user=>

cons is acting according to its documentation.

Some of the roles played by lists in other Lisps are played by seqs in
Clojure. Also, in Clojure a list is not a linked list of cons cells.
Roughly speaking, a Clojure list is a linked list of lists:

user=> (list? (rest (rest (rest '(1 2 3 4 5 6 7)))))
true

--Steve

Handkea fumosa

unread,
Jun 28, 2009, 1:45:40 AM6/28/09
to Clojure
It's list? that isn't.

> Some of the roles played by lists in other Lisps are played by seqs in  
> Clojure. Also, in Clojure a list is not a linked list of cons cells.  
> Roughly speaking, a Clojure list is a linked list of lists:
>
>    user=> (list? (rest (rest (rest '(1 2 3 4 5 6 7)))))
>    true

My current workaround is to use

(defn fixed-list? [obj]
(and (sequential? obj) (not (vector? obj))))

This gives me the desired semantics: it's a list if it's a ()
collection rather than a [], {}, or #{} collection.

Richard Newman

unread,
Jun 28, 2009, 1:49:50 AM6/28/09
to clo...@googlegroups.com
>> cons is acting according to its documentation.
>
> It's list? that isn't.

That's not strictly true:

user=> (doc list?)
-------------------------
clojure.core/list?
([x])
Returns true if x implements IPersistentList
nil
user=> (cons 5 '(1 2 3))
(5 1 2 3)
user=> (ancestors (type *1))
#{clojure.lang.IMeta java.lang.Iterable java.util.Collection
clojure.lang.ISeq java.util.List clojure.lang.Seqable clojure.lang.Obj
clojure.lang.IObj java.lang.Object clojure.lang.Sequential
clojure.lang.IPersistentCollection clojure.lang.Streamable
clojure.lang.ASeq}


... the result of cons does not implement IPersistentList, so list? is

acting according to its documentation.

Is there a reason why you are testing for listiness rather than for
some other property, like Sequential? It's generally considered good
form in Clojure to program against the native abstractions (such as
sequences) rather than particular concrete types.

-R

Meikel Brandmeyer

unread,
Jun 28, 2009, 2:26:04 AM6/28/09
to clo...@googlegroups.com
Hi,

Am 28.06.2009 um 07:45 schrieb Handkea fumosa:

> It's list? that isn't.

No. list? is not broken. Every list is a seq, but not
every seq is a list.

Consider: (cons 0 (iterate inc 1))

This is no list! It's a sequence. Why should list?
return true?

In Clojure there is no such thing as a cons cell.
There is only an abstraction of a cons cell where
the second part must implement again the
sequence abstraction.

If you need a cons cell, use a two-element vector
or a two-element list.

Please: If you want to program CL in Clojure...
Don't do it. Clojure is different. Claiming parts
of Clojure are broken because they don't behave
like something is else doesn't make sense and
hence leads nowhere.

If you want to try Clojure, make up your mind
and learn Clojure's environment. Thinking
Python/Perl/CL/whatever in Clojure will only
hinder your progress with Clojure.

If you find a bug, where a function violates its
documented contract, please report it.

Sincerely
Meikel

PS: This is no Clojure specific issue. Trying to
learn Python and program it like one would in
Clojure, also doesn't work...

Rich Hickey

unread,
Jun 28, 2009, 11:21:02 AM6/28/09
to clo...@googlegroups.com
On Sun, Jun 28, 2009 at 2:26 AM, Meikel Brandmeyer<m...@kotka.de> wrote:
> Hi,
>
> Am 28.06.2009 um 07:45 schrieb Handkea fumosa:
>
>> It's list? that isn't.
>
> No. list? is not broken. Every list is a seq, but not
> every seq is a list.
>
> Consider: (cons 0 (iterate inc 1))
>
> This is no list! It's a sequence. Why should list?
> return true?
>
> In Clojure there is no such thing as a cons cell.
> There is only an abstraction of a cons cell where
> the second part must implement again the
> sequence abstraction.
>
> If you need a cons cell, use a two-element vector
> or a two-element list.
>

This is overstating things. There certainly is a cons cell, it's
called Cons, and cons produces one. It differs from CL cons cell in
not being an arbitrary pair (i.e. the rest must be a sequence or nil).

> Please: If you want to program CL in Clojure...
> Don't do it. Clojure is different. Claiming parts
> of Clojure are broken because they don't behave
> like something is else doesn't make sense and
> hence leads nowhere.
>

This too is a bit much. The OP wasn't trying to use cons as a pair,
just expecting list? to be more similar to listp. It's a reasonable
mistake, please be gentle.

Because Clojure supports a seq abstraction, there is heterogeneity in
the implementations. In fact there are two cons-cell-like data
structures:

user=> (class (cons 4 '(1 2 3)))
clojure.lang.Cons
user=> (counted? (cons 4 '(1 2 3)))
false

user=> (class (conj '(1 2 3) 4))
clojure.lang.PersistentList
user=> (counted? (conj '(1 2 3) 4))
true

So, list? is more specific because seqs are more general. seq? is
probably what is desired.

cons onto an IPersistentList could be made to return another
IPersistentList, but currently doesn't.

Rich

Handkea fumosa

unread,
Jun 28, 2009, 11:52:56 AM6/28/09
to Clojure
On Jun 28, 1:49 am, Richard Newman <holyg...@gmail.com> wrote:
> >> cons is acting according to its documentation.
>
> > It's list? that isn't.
>
> That's not strictly true

Are you calling me a liar?

> Is there a reason why you are testing for listiness rather than for  
> some other property, like Sequential? It's generally considered good  
> form in Clojure to program against the native abstractions (such as  
> sequences) rather than particular concrete types.

It's for a code-manipulating macro. It needs to treat () colls
differently from [] ones, in particular. I note that sequential?
returns true for vectors.

While it starts with inputs like '(foo bar (baz quux)) it takes those
apart and puts them back together again in various ways, sometimes
using cons to prepend an item. It also sometimes uses map, whose
return values also fail list?.

If you don't think list? should be true for all () collections, or at
least all finite ones, perhaps the core should contain a function that
is? We have set?, vector?, and map? for [], #{}, and {} collections
respectively that exactly distinguish them from others. But for ()
collections we only seem to have #(and (sequential? %) (not (vector?
%))).

Rich Hickey

unread,
Jun 28, 2009, 12:02:52 PM6/28/09
to Clojure
seq? is what you want.

Rich

Handkea fumosa

unread,
Jun 28, 2009, 12:03:42 PM6/28/09
to Clojure
On Jun 28, 11:21 am, Rich Hickey <richhic...@gmail.com> wrote:
> This too is a bit much. The OP wasn't trying to use cons as a pair,
> just expecting list? to be more similar to listp. It's a reasonable
> mistake, please be gentle.

If you're referring to me, I don't agree that it is a mistake to
expect that there'd be a correspondence between (1 2 3), [1 2 3], #{1
2 3), {:one 1 :two 2 :three 3} and list?, vector?, set?, and map?

> Because Clojure supports a seq abstraction, there is heterogeneity in
> the implementations.

Heterogeneity that often ought to be hidden from the user.

The major way in which a seq could fail to be list-like is if it were
infinite. Unfortunately there doesn't seem at present to be an
efficient infinite? predicate. (Actually, for custom uses of lazy-seq
that might run afoul of the so-called halting problem. Perhaps a
possibly-infinite? predicate which could return true for the results
of lazy-seq, repeatedly, iterate, and their ilk, false for the results
of take, list, and the like, and the same value as for the input seq
in the case of map, cons, and the like; for for, it would be true if
any of the input colls was possibly-infinite? and for interleave,
false if any were not. Then a test for "listiness" could check for
sequential?, not vector?, and not possibly-infinite?.

Implementing the above would require adding an isPossiblyInfinite
method to some of the clojure.lang interfaces and classes, and in some
cases a field for this to return, with code to compute its value in
the constructor.

> So, list? is more specific because seqs are more general. seq? is
> probably what is desired.

That does seem to be false for vectors and true for the () colls I
threw at it, including a quoted list, a cons, and a map output.

Is it equivalent to "is a () coll"?

Richard Newman

unread,
Jun 28, 2009, 12:14:21 PM6/28/09
to clo...@googlegroups.com
>>> It's list? that isn't.
>>
>> That's not strictly true
>
> Are you calling me a liar?

Not a liar; just misinformed, as I hope I demonstrated by citing the
docs. I don't see any value in continuing this thread of the
discussion, but I wanted to clear that up.

> If you don't think list? should be true for all () collections, or at
> least all finite ones, perhaps the core should contain a function that
> is? We have set?, vector?, and map? for [], #{}, and {} collections
> respectively that exactly distinguish them from others. But for ()
> collections we only seem to have #(and (sequential? %) (not (vector?
> %))).

As Rich pointed out, seq? is what you want.

user=> (seq? [])
false
user=> (cons 5 [])
(5)
user=> (seq? *1)
true

Handkea fumosa

unread,
Jun 28, 2009, 12:50:27 PM6/28/09
to Clojure
On Jun 28, 12:14 pm, Richard Newman <holyg...@gmail.com> wrote:
> >>> It's list? that isn't.
>
> >> That's not strictly true
>
> > Are you calling me a liar?
>
> Not a liar; just misinformed

I don't agree.
Reply all
Reply to author
Forward
0 new messages