Idiomatic way to get first element of a lazy seq in clojure
clojure, lazy-sequences
Solution
The docstrings for `first` and `rest` say that these functions call `seq` on their arguments to convey the idea that you don't have to call `seq` yourself when passing in a seqable collection which is not in itself a seq, like, say, a vector or set. For example,
(first [1 2 3])
;= 1
would not work if `first` didn't call `seq` on its argument; you'd have to say
(first (seq [1 2 3]))
instead, which would be inconvenient.
Both `take` and `drop` also call `seq` on their arguments, otherwise you couldn't call them on vectors and the like as explained above. In fact this is true of all standard seq collections -- those which do not call `seq` directly are built upon lower-level components which do.
In no way does this impair the laziness of lazy seqs. The forcing / realization which happens as a result of a `first` / `rest` call is the smallest amount possible to obtain the requested result. (How much that is depends on the type of the argument; if it is not in fact lazy, there is no extra realization involved in the `first` call; if it is partly lazy -- that is, chunked -- there will be some extra realization (up to 32 initial elements will be computed at once); if it's fully lazy, only the first element will be computed.)
Clearly `first`, when passed a lazy seq, must force the realization of its first element -- that's the whole point. `rest` is actually somewhat lazy in that it actually doesn't force the realization of the "rest" part of the seq (that's in contrast to `next`, which is basically equivalent to `(seq (rest ...))`). The fact that it does force the first element to be realized so that it can skip over it immediately is a conscious design choice which avoids unnecessary layering of lazy seq objects and holding the head of the original seq; you could say something like `(lazy-seq (rest xs))` to defer even this initial realization, at the cost of holding on to `xs` until realized the lazy seq wrapper is realized.
Problem
When processing each element in a `seq` I normally use first and rest. However these will cause a `lazy-seq` to lose its "laziness" by calling `seq` on the argument. My solution has been to use `(first (take 1 coll))` and `(drop 1 coll)` in their place when working with `lazy-seq`s, and while I think `drop 1` is just fine, I don't particularly like having to call `first` and `take` to get the first element. Is there a more idiomatic way to do this?