Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't you rseq an RSeq?

user=> (rseq [:a :b])
(:b :a)
user=> (rseq (rseq [:a :b]))
ClassCastException clojure.lang.APersistentVector$RSeq cannot be cast to
  clojure.lang.Reversible  clojure.core/rseq (core.clj:1532)

Why can't rseq accept the result of a previous call to rseq?

I read in the docstring that the argument has to be (actually, "can be") a vector or sorted-map, and the above shows that it can't be an RSeq, so I already know that. What I want to know is: is there a good reason for this restriction? Is it just an oversight, or does this restriction provide some important benefit?

Also, is there a convenient workaround for this, other than just never calling rseq? It's hard to know, when you return an RSeq from one function, whether some other function somewhere else might call rseq on it.

I'm asking because it's frustrating to see my code throw exceptions for such surprising reasons. If I knew why this made sense, I might be less likely to make this and similar kinds of errors.

like image 806
Ben Kovitz Avatar asked May 25 '16 08:05

Ben Kovitz


1 Answers

You cannot call rseq on a seq, since you need an input collection with constant-time random access to fullfill rseq's constant-time performance characteristics, and seqs only provide efficient access (iteration) from the head down.

Calling rseq on the result of rseq cannot be special-cased to return the original collection since the original collection is never a seq. And if calling rseq on a RSeq would return something (seq coll), that won't make it straightforward to support (rseq (drop x (rseq coll))). It's probably those kinds of complications that kept the language implementers from supporting "recursive" rseq at all.

If you need a general reversal function, use reverse - which will be slower. If you can, you probably just want to keep a reference to (seq coll) and (rseq coll) if you need both.

like image 80
Joost Diepenmaat Avatar answered Nov 12 '22 06:11

Joost Diepenmaat