Search code examples
clojure

Why can't I peek a sorted seq in Clojure?


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!


Solution

  • Copying and slightly editing (striking the mention of conj because it's incorrect) a good comment by @potetm on Clojurians Slack:

    cons, seq, map, and filter 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, and PersistentQueue 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 conj are queue functions. They’re only available on concrete data types which implement the IPersistentStack interface. (That includes lists, vectors, and PersistentQueue.) This restriction is to quasi-guarantee that peek and pop 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).