Cons bug?

19 views
Skip to first unread message

Henk Boom

unread,
Dec 11, 2007, 2:52:31 AM12/11/07
to clo...@googlegroups.com
Found this while trying to use apply with a vector as the last argument:

user=> (cons 1 [2])
java.lang.ClassCastException: clojure.lang.PersistentVector cannot be
cast to clojure.lang.ISeq
at clojure.cons.invoke(boot.clj:13)
at clojure.lang.AFn.applyToHelper(Unknown Source)
at clojure.lang.AFn.applyTo(Unknown Source)
at clojure.lang.Compiler$InvokeExpr.eval(Unknown Source)
at clojure.lang.Compiler.eval(Unknown Source)
at clojure.lang.Repl.main(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at jline.ConsoleRunner.main(ConsoleRunner.java:69)

Henk

Rich Hickey

unread,
Dec 11, 2007, 7:32:11 AM12/11/07
to Clojure


On Dec 11, 2:52 am, "Henk Boom" <lunarcri...@gmail.com> wrote:
> Found this while trying to use apply with a vector as the last argument:
>
> user=> (cons 1 [2])
> java.lang.ClassCastException: clojure.lang.PersistentVector cannot be
> cast to clojure.lang.ISeq


This is by design. cons only works with seqs. You can get a seq on a
vector with the seq function:

(cons 1 (seq [2]))

This isn't done automatically in order to prevent accidents (using
cons where conj would be correct).

Rich

Henk Boom

unread,
Dec 11, 2007, 2:15:47 PM12/11/07
to clo...@googlegroups.com
On 11/12/2007, Rich Hickey <richh...@gmail.com> wrote:
> This is by design. cons only works with seqs. You can get a seq on a
> vector with the seq function:
>
> (cons 1 (seq [2]))
>

I rewrote this e-mail several times as I discovered I understood less
and less about collections and sequences than I had thought.

I had understood from the documentation that sequences were a
super-type of collections, and that first and rest operated on
sequences (they are introduced as "the sequence interface"). It seems
it's the other way around, though, as from looking at the code I see
that sequences are a sub-type of collections, and that first and rest
are actually functions on collections (though rest is collection ->
sequence), not necessarily sequences. Am I correct in saying that
sequences are simply collections which can be used with cons? I hear
about sequences in Clojure much more often than I hear about
collections, so it's a little surprising to see this.

Also, returning to my original problem, why does apply only take seqs
as the final argument?

Henk

Rich Hickey

unread,
Dec 11, 2007, 3:42:05 PM12/11/07
to Clojure


On Dec 11, 2:15 pm, "Henk Boom" <lunarcri...@gmail.com> wrote:
> On 11/12/2007, Rich Hickey <richhic...@gmail.com> wrote:
>
> > This is by design. cons only works with seqs. You can get a seq on a
> > vector with the seq function:
>
> > (cons 1 (seq [2]))
>
> I rewrote this e-mail several times as I discovered I understood less
> and less about collections and sequences than I had thought.
>

Sorry if this is confusing. I'll try to clarify, but I will say right
off that super- and sub- type notions aren't going to be as useful as
you might think.

If you look at the docs for Collections you'll see that they all must
support 3 functions:

count
conj
seq

and that seq returns an ISeq instance (Java interface) _on_ the
collection. That is the critical point - a seq is a logical sequential
_view_ of a collection and not (necessarily) the collection itself.
There are about 20 concrete implementations of the ISeq interface in
Clojure, supporting sequential access to all of its data structures.
One way to think of the seq on a collection is as a cursor or iterator
over the collection.

The ISeq Java interface in turn has 2 _methods_, first and rest,
provided by all implementations.

The Clojure library has 2 _functions_, first and rest, that are
defined on _collections_ and specified to first call seq on their
argument. Calling seq on something that is already a seq just returns
it.

So [1 2 3] is not a seq, nor is {:a 1 :b 2}, but first and rest and
most of the sequence library work on them because they first obtain a
seq on the collection using the seq function. Otherwise it would be
quite tedious to have to call seq everywhere. Thus, while they are
functions of collections (i.e. they take collections), they operate on
the sequential views of those collections, thus the name sequence
functions.

Some collections are their own sequential view, i.e. they implement
ISeq directly because they inherently have linked structure (e.g.
lists):

(instance? [1 2 3] clojure.lang.ISeq)
->nil
(instance? '(1 2 3) clojure.lang.ISeq)
->:t

(. [1 2 3] (getClass))
-> class clojure.lang.PersistentVector

(. (seq [1 2 3]) (getClass))
-> class clojure.lang.PersistentVector$Seq

(. '(1 2 3) (getClass))
-> class clojure.lang.PersistentList

(. (seq '(1 2 3)) (getClass))
-> class clojure.lang.PersistentList

Of course, it's quite straightforward for ISeqs to behave like
collections (i.e. implement count, conj and seq), and so they do, etc
- the Clojure library is designed to be maximally polymorphic. In the
end, unless you are interested in extending the sequence library in
Java, it's not important to know the type hierarchy. Generally, all
the sequence functions work on all of the collections. cons is an
exception because it creates linked structure, and I want to avoid the
accidental creation of linked structure on top of associative
structure, whereas all of the other sequence functions are view-only
or create independent structure. Not that there's anything wrong with
(cons 1 (seq {:a 1 :b 2 :c 3})) if that's what the user really
intends.

> Also, returning to my original problem, why does apply only take seqs
> as the final argument?
>

There is no good reason - I have fixed this so it takes any collection
as the last arg (and calls seq on it!) - thanks for the report.

Rich

Henk Boom

unread,
Dec 11, 2007, 5:53:54 PM12/11/07
to clo...@googlegroups.com
On 11/12/2007, Rich Hickey <richh...@gmail.com> wrote:
> ...

Thanks for the explanation, it makes more sense now =).

> > Also, returning to my original problem, why does apply only take seqs
> > as the final argument?
> >
>
> There is no good reason - I have fixed this so it takes any collection
> as the last arg (and calls seq on it!) - thanks for the report.

Cool, thanks!

Henk

Reply all
Reply to author
Forward
0 new messages