while practicing with Clojure I noticed this. Peeking a vector works as expected:
(let [v (vector 1 2 3)]
(peek v))
;; => 3
Peeking a list too:
(let [l (list 1 2 3)]
(peek l))
;; => 1
However, if I first sort the sequence, it panics!
(let [l (sort (list 1 2 3))]
(peek l))
;; => Unhandled java.lang.ClassCastException
class clojure.lang.ArraySeq cannot be cast to class
clojure.lang.IPersistentStack (clojure.lang.ArraySeq and
clojure.lang.IPersistentStack are in unnamed module of loader
'app')
Why is that? Thanks!
Copying and slightly editing (striking the mention of conj
because it's incorrect) a good comment by @potetm on Clojurians Slack:
cons
,seq
,map
, andfilter
are sequence abstractions. They’re designed to be lazy. They can be backed by any number of concrete data types or, potentially, none at all (e.g. infinite sequences).
list
,vector
, andPersistentQueue
are all concrete data types. They are designed to hold all contents in memory and to be very efficient at various operations, depending on the type. For example,list
has a very efficient prepend operation, but poor indexed access.vector
has a very efficient append operation and very efficient indexed access.In clojure,
peek
,pop
, and conjare queue functions. They’re only available on concrete data types which implement theIPersistentStack
interface. (That includes lists, vectors, and PersistentQueue.) This restriction is to quasi-guarantee thatpeek
andpop
will be extremely efficient (usually requiring no more than a single allocation). This also guarantees that each function keeps the same data type as the original collection (unlike sequence functions).