Lazy-seq-cons outside or in

Should exist inside (lazy-seq ...)

(def lseq-in (lazy-seq (cons 1 (more-one)))) 

or out?

 (def lseq-out (cons 1 (lazy-seq (more-one)))) 

I noticed

 (realized? lseq-in) ;;; β‡’ false (realized? lseq-out) ;;; β‡’ <err> ;;; ClassCastException clojure.lang.Cons cannot be cast to clojure.lang.IPending clojure.core/realized? (core.clj:6773) 

All examples at clojuredocs.org use "out".

What are the tradeoffs?

+6
source share
2 answers

You definitely want (lazy-seq (cons ...)) the default, deviating only if you have a clear reason for this. clojuredocs.org is fine, but the examples are all provided by the community, and I would not call them "docs." Of course, the consequence of how it is built is that examples are usually written by people who have just learned to use this design and want to help, so many of them are poor. Instead, I would refer to the code in clojure.core or other well-known code.

Why should this be the default? Consider these two map implementations:

 (defn map1 [f coll] (when-let [s (seq coll)] (cons (f (first s)) (lazy-seq (map1 f (rest coll)))))) (defn map2 [f coll] (lazy-seq (when-let [s (seq coll)] (cons (f (first s)) (map2 f (rest coll)))))) 

If you call (map1 prn xs) , then the xs element will be implemented and printed immediately, even if you never intentionally implement the element of the resulting displayed sequence. map2 , on the other hand, immediately returns a lazy sequence, delaying all its work until an element is requested.

+8
source

With cons inside lazy-seq evaluating the expression for the first element of your seq is deferred; with cons outside, this was done right away, and only the construction of the "rest" of the seq was postponed. (So ​​( (rest lseq-out) will be lazy seq.)

Thus, if calculating the first element is expensive and might not be necessary at all, using cons inside lazy-seq makes more sense. If the source element is provided to the lazy seq producer as an argument, it might make sense to use cons externally (in this case with clojure.core/iterate ). Otherwise, it does not really matter. (The overhead of creating a lazy seq object at the beginning is negligible.)

Clojure itself uses both approaches (although in most cases lazy-seq wraps an entire seq expression that may not start with cons ).

+7
source

Source: https://habr.com/ru/post/946900/


All Articles