(cons 1 '()) broken

6 views
Skip to first unread message

Feng

unread,
May 28, 2008, 11:43:47 PM5/28/08
to Clojure
Hi,

(cons 1 '()) broken in svn revision 883. Is it a regression or
intentional ?

user=> (cons 1 '())
java.lang.ClassCastException: clojure.lang.PersistentList$EmptyList
java.lang.ClassCastException: clojure.lang.PersistentList$EmptyList
at clojure.fns.clojure.cons__1.invoke(boot.clj:21)
at clojure.lang.AFn.applyToHelper(AFn.java:186)
at clojure.lang.AFn.applyTo(AFn.java:175)
at clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:2412)
at clojure.lang.Compiler.eval(Compiler.java:3501)
at clojure.lang.Repl.main(Repl.java:75)
user=>

regards,
Feng

Rich Hickey

unread,
May 29, 2008, 8:21:53 AM5/29/08
to Clojure


On May 28, 11:43 pm, Feng <hou...@gmail.com> wrote:
> Hi,
>
> (cons 1 '()) broken in svn revision 883. Is it a regression or
> intentional ?
>

That is intentional. The empty list - () had been erroneously
considered a seq. Non-empty lists are their own seq, but the empty
list shouldn't have been. It now behaves the same as [] and {} in many
situations, including this. conj and pop are the proper way to use a
list like a collection.

Note that this is orthogonal to whether or not cons accepts a
collection as its second argument. Currently it does not, it requires
a seq, the intention being to avoid accidentally using cons when conj
was intended.

cons could call seq on its second argument, and in doing so would be
like the other seq-consuming functions, at the risk mentioned above.

I don't have strong feelings about this last point either way -
feedback welcome.

Rich

Feng

unread,
May 29, 2008, 2:53:43 PM5/29/08
to Clojure
Personally, I like to use cons/first/rest operators when I *know* I'm
working with list (I like the concept of seq *is* the abstract
interface of list very much). Somehow, I feel conj etc is too
polymorphic (i.e. too abstract) in this case.

With the new semantics, this works.

(let [lst nil] ... (cons 'a lst) ...)

but not,

(let [lst '()] ... (cons 'a lst) ...)

I don't like nil bound var much.

> Rich

Thanks for sharing your thoughts!
Feng

James Reeves

unread,
May 29, 2008, 4:32:38 PM5/29/08
to Clojure
On May 29, 7:53 pm, Feng <hou...@gmail.com> wrote:
> With the new semantics, this works.
>
> (let [lst nil] ... (cons 'a lst) ...)
>
> but not,
>
> (let [lst '()] ... (cons 'a lst) ...)
>
> I don't like nil bound var much.

I don't understand why Clojure distinguishes between '() and nil.

--
James Reeves

jon

unread,
May 29, 2008, 4:49:21 PM5/29/08
to Clojure
> cons could call seq on its second argument, and in doing so would be
> like the other seq-consuming functions, at the risk mentioned above.
>
> I don't have strong feelings about this last point either way -
> feedback welcome.

My vote is for the status quo, for the reason you give.. encouraging
correct use of conj.
Jon

Randall R Schulz

unread,
May 29, 2008, 5:00:40 PM5/29/08
to clo...@googlegroups.com
On Thursday 29 May 2008 13:32, James Reeves wrote:
> ...

>
> I don't understand why Clojure distinguishes between '() and nil.

You think of an empty list as being indistinguishable from NIL??


> --
> James Reeves


Randall Schulz

Raoul Duke

unread,
May 29, 2008, 5:03:51 PM5/29/08
to clo...@googlegroups.com
>> I don't understand why Clojure distinguishes between '() and nil.
> You think of an empty list as being indistinguishable from NIL??

http://cs-www.cs.yale.edu/homes/dvm/nil.html

Randall R Schulz

unread,
May 29, 2008, 5:46:52 PM5/29/08
to clo...@googlegroups.com

I take it that's your way of agreeing with my (implicit) statement that
it is counterintuitive to conflate nothingness with an empty list?


Randall Schulz

Raoul Duke

unread,
May 29, 2008, 5:58:09 PM5/29/08
to clo...@googlegroups.com
>> http://cs-www.cs.yale.edu/homes/dvm/nil.html

> I take it that's your way of agreeing with my (implicit) statement that
> it is counterintuitive to conflate nothingness with an empty list?

yup (sorry for the obtuseness).

Feng

unread,
May 29, 2008, 6:57:54 PM5/29/08
to Clojure
You meant the status quo of svn revision 883, right?

It's a little vague what Rich meant by "encouraging correct use of
conj". Does it mean encouraging "list as coll (conj/pop/peek)" rather
than "list as seq (cons/first/rest)"? I thought Rich promote "list as
seq as cons" in his seq screencast.

I'm neutral on () is not seq, but prefer not breaking (cons 1 '()).
This is what "cons could call seq on its second argument" is meant
for, correct?

Rich, can you clarify?

> Jon

Rich Hickey

unread,
May 29, 2008, 7:32:28 PM5/29/08
to Clojure


On May 29, 6:57 pm, Feng <hou...@gmail.com> wrote:
> On May 29, 4:49 pm, jon <superuser...@googlemail.com> wrote:
>
> > > cons could call seq on its second argument, and in doing so would be
> > > like the other seq-consuming functions, at the risk mentioned above.
>
> > > I don't have strong feelings about this last point either way -
> > > feedback welcome.
>
> > My vote is for the status quo, for the reason you give.. encouraging
> > correct use of conj.
>
> You meant the status quo of svn revision 883, right?
>
> It's a little vague what Rich meant by "encouraging correct use of
> conj". Does it mean encouraging "list as coll (conj/pop/peek)" rather
> than "list as seq (cons/first/rest)"? I thought Rich promote "list as
> seq as cons" in his seq screencast.
>

The cons/conj thing is about other data structures, esp. vectors.

Given:

user=> (def v [1 2 3])
#'user/v

user=> (conj v 4)
[1 2 3 4]

Right now, this is an error:

user=> (cons 4 v)
java.lang.ClassCastException: clojure.lang.PersistentVector

but if cons called seq on its second arg, it would 'work' like this:

user=> (cons 4 v)
(4 1 2 3) ;oops

> I'm neutral on () is not seq, but prefer not breaking (cons 1 '()).
> This is what "cons could call seq on its second argument" is meant
> for, correct?

Right, it would make that work. FYI, there's no need to quote ().

Rich

Rich Hickey

unread,
May 29, 2008, 7:36:27 PM5/29/08
to Clojure
For the same reason it distinguishes between [] and nil, and {} and
nil.

Rich

Stephen C. Gilardi

unread,
May 29, 2008, 7:43:55 PM5/29/08
to clo...@googlegroups.com
Note that this is orthogonal to whether or not cons accepts a
collection as its second argument. Currently it does not, it requires
a seq, the intention being to avoid accidentally using cons when conj
was intended.

cons could call seq on its second argument, and in doing so would be
like the other seq-consuming functions, at the risk mentioned above.

I don't have strong feelings about this last point either way -
feedback welcome.

A cons that called seq on its second argument would mean "put this on the front of this seq".  I like that.

Is it correct that the risk you're talking about is that something like:

    (cons 1 [2 3 4])

would work and (perhaps unintentionally) yield a seq, while

    (conj [2 3 4] 1)

would also work, but would (perhaps as intended) yield a vector?

Even in the face of that risk, I still think cons calling seq on its second argument is a good choice.  This would have the effect that (cons 1 ()) would work which I like because this series of steps shouldn't "blow up" when the list gets empty as it does currently:

  (cons 1 '(2 3 4))
  (cons 1 '(2 3))
  (cons 1 '(2))
  (cons 1 '())

I think lazy-cons should behave the same way as cons does in the regard.  Currently lazy-cons appears not to call seq on its second argument.  I think it would be an improvement if it did.

I also noticed that the empty list doesn't currently have a succinct printer representation.  Since "()" works to produce one, I think "()" (without quotes) is a good one for it.  It's consistent with [] {} #{}.

--Steve

Feng

unread,
May 29, 2008, 10:55:44 PM5/29/08
to Clojure


On May 29, 7:32 pm, Rich Hickey <richhic...@gmail.com> wrote:
>
> The cons/conj thing is about other data structures, esp. vectors.
>
> Given:
>
> user=> (def v [1 2 3])
> #'user/v
>
> user=> (conj v 4)
> [1 2 3 4]
>
> Right now, this is an error:
>
> user=> (cons 4 v)
> java.lang.ClassCastException: clojure.lang.PersistentVector
>
> but if cons called seq on its second arg, it would 'work' like this:
>
> user=> (cons 4 v)
> (4 1 2 3) ;oops
>

I don't really like it either. That's why I stay neutral on "() is not
seq" change.

Does this really prevent from erroneous use of () in practice (other
that conceptual cleanness)?

It seems break more things than it fixed.

1. It breaks existing cons without introducing the new erroneous usage
you explained above.

2. It breaks following series of invariants as well. Though I don't
know when I'd rely on it, but it certainly looks neat.

user> (def alist '(1 2 3))
#'user/alist
user> (identical? alist (seq alist))
true
user> (identical? (seq alist) (seq (seq alist)))
true
...

user> (def elist '())
#'user/elist
user> (identical? elist (seq elist))
false ; flip??
user> (identical? (seq elist) (seq (seq elist)))
true ; flop??

> > I'm neutral on () is not seq, but prefer not breaking (cons 1 '()).
> > This is what "cons could call seq on its second argument" is meant
> > for, correct?
>
> Right, it would make that work. FYI, there's no need to quote ().
>

Thanks you for pointing it out. I'm used to write '() in data, write
() only in macro call.

> Rich

regards,
Feng

Feng

unread,
May 29, 2008, 11:05:40 PM5/29/08
to Clojure


On May 29, 10:55 pm, Feng <hou...@gmail.com> wrote:

> 2. It breaks following series of invariants as well. Though I don't
> know when I'd rely on it, but it certainly looks neat.

> user> (def elist '())
> #'user/elist
> user> (identical? elist (seq elist))
> false ; flip??
> user> (identical? (seq elist) (seq (seq elist)))
> true ; flop??
>

scratch that. nil is not (), which I like. Sorry, I confused
myself ;-)

Feng

Rich Hickey

unread,
May 30, 2008, 7:57:29 AM5/30/08
to Clojure


On May 29, 7:43 pm, "Stephen C. Gilardi" <scgila...@gmail.com> wrote:

> I also noticed that the empty list doesn't currently have a succinct
> printer representation. Since "()" works to produce one, I think
> "()" (without quotes) is a good one for it. It's consistent with []
> {} #{}.
>

Fixed - thanks for the report,

Rich

Brian Watkins

unread,
May 30, 2008, 1:01:45 PM5/30/08
to Clojure
In a language based on cons cells it only makes sense that nil and '()
should be
exactly the same thing. But Clojure doesn't have cons cells. It's a
lisp without
lists. At least, without traditional lists; they've been replaced
with abstract Java
lists.

It feels very lispy (McCarthyite?) to shift the paradigm underneath
and keep the
power of the forms.

-B
Reply all
Reply to author
Forward
0 new messages