For collections and sequences, when calling str
on them, Clojure will return an EDN-like
representation of them as string more similar to the result of pr-str
:
(str (vector 1 2 3))
"[1 2 3]"
(str '(1 2 3))
"(1 2 3)"
(str (seq [1 2 3]))
"(1 2 3)"
(str {:a 1 :b 2})
"{:a 1, :b 2}"
(str #{1 2})
"#{1 2}"
But when used on lazy-seq
, str
will print the type of lazy-seq
followed by the hash of its realized values:
(str (lazy-seq [1 2 3]))
"clojure.lang.LazySeq@7861"
Not only could this be argued to be a rather useless behavior for most use case (as most people probably would rather it stringified the same as seq
does. It also force realizes the lazy-seq
:
(let [ls (map inc [1 2 3])]
(realized? ls))
;;=> false
(let [ls (map inc [1 2 3])]
(str ls)
(realized? ls))
;;=> true
Similarly, when used on eductions
, str
will print the type of eduction
this time followed by the memory location:
(str (eduction identity [1 2 3]))
"clojure.core.Eduction@2a85e4f4"
Unlike for lazy-seq
, it will not "realize" the eduction:
(str (eduction (fn[e] (println e) (identity e)) [1 2 3]))
"clojure.core.Eduction@26ae9861"
(str (map (fn[e] (println e) (identity e)) [1 2 3]))
1
2
3
"clojure.lang.LazySeq@7861"
It seems like the current behavior for lazy-seq
and eduction
is either inconsistent or wrong.
Ideally it would behave either where:
str
would cause neither lazy-seq
nor eduction
to realize themselves, but would still stringify as only their type. The idea here would be that str
does not realize "pending" computation, thus it would be safe to use on infinite lazy-seqs
or repeatedly on eductions
.
- Or
str
would always realize the "pending computation", and thus lazy-seq
and eduction
would both stringify the same as other collections and sequences do, more similar to calling pr-str
on them.
P.S.: It might be relevant when making a decision to consider that ClojureScript currently behaves differently in that it does option 2 for lazy-seq
and I'm not sure what it does for eduction
:
cljs.user=> (str (lazy-seq [1 2 3]))
"(1 2 3)"
cljs.user=> (str (eduction identity [1 2 3]))
"[object Object]"
P.S.2: It also appears the behavior when a lazy-seq
or an eduction
is nested inside another collection or sequence differs in that in those scenarios, they will get stringified in an EDN-like
fashion:
(str [1 (map identity [2 3]) 4])
"[1 (2 3) 4]"
(str (seq [1 (map identity [2 3]) 4]))
"(1 (2 3) 4)"
(str [1 (eduction identity [2 3]) 4])
"[1 (2 3) 4]"
(str (seq [1 (eduction identity [2 3]) 4]))
"(1 (2 3) 4)"
This seems to be the case in ClojureScript as well.