> Richard---
> It's not the same thing:
>
> (class (doall (map (fn [x] x) [1 2 3])))
> -> clojure.lang.LazySeq
>
> whereas
>
> (class (binding [*strict* true]
> (map (fn[x] x) [1 2 3])))
> -> clojure.lang.LazilyPersistentVector
From Clojure's perspective those *are* the same thing:
user=> (= (map identity [1 2 3]) [1 2 3])
true
Also, just because it's lazy doesn't mean it hasn't already done the
work, and
user=> (def *x* 1)
#'user/*x*
user=> (doseq [y (map (fn [x] (* *x* x)) [1 2 3])]
(println y))
1
2
3
nil
user=> (doseq [y (binding [*x* 2]
(map (fn [x] (* *x* x)) [1 2 3]))]
(println y))
1
2
3
nil
user=> (doseq [y (binding [*x* 2]
(doall
(map (fn [x] (* *x* x)) [1 2 3])))]
(println y))
2
4
6
nil
The result of doall is a sequence that has already been evaluated.
If you want a concrete sequence, you can get one: simply call seq on
the output, or use vec or into.
user=> (type (binding [*x* 2] (doall (map (fn [x] (* *x* x)) [1 2 3]))))
clojure.lang.LazySeq
user=> (type (seq (binding [*x* 2] (doall (map (fn [x] (* *x* x)) [1 2
3])))))
clojure.lang.ChunkedCons
user=> (type (vec (binding [*x* 2] (doall (map (fn [x] (* *x* x)) [1 2
3])))))
clojure.lang.PersistentVector
user=> (vec (binding [*x* 2] (doall (map (fn [x] (* *x* x)) [1 2 3]))))
[2 4 6]
> Also, having a dynamic var that turns laziness on and off would allow
> you to do it once for any given scope, without having to add the extra
> 'ceremony' of doalls.
Sure, but it would either require complicating the core, or making
functions that use lazy-seq pay attention to the binding of that var.
Not fun -- it would introduce a problem that library authors have to
think about.
> I would add to that that casting your types back to what they were is
> also unrealistic.
I get the impression that programming with *abstractions* is the
Clojure way. Dependence on particular sequence types is usually bad.
If you need to, though, it's only a vec call away. If you call vec
inside a binding form, you don't even need to realize the lazy
sequence yourself.
> If there were a *strict* dynamic var, then you \could choose code
> simplification over laziness with a single line. What would be wrong
> with that?
To play devil's advocate: it complicates the implementation (and the
implementation of libraries); the strictness would be undesirably
viral (what if you accidentally cause a library to realize an infinite
lazy sequence?); and for all I know it would screw up JIT optimization.
My opinion is that an easier way to realize nested lazy sequences
would be a more elegant solution to your problem; doall*, say. This
could almost be implemented as
(defmulti doall* class)
(defmethod doall* clojure.lang.LazySeq [x]
(seq (doall (map doall* x))))
(defmethod doall* :default [x] x)
...
;; Make some nested LazySeqs.
user=> (type (map (fn [x] (repeat x :foo)) [1 2 3]))
clojure.lang.LazySeq
user=> (map type (map (fn [x] (repeat x :foo)) [1 2 3]))
(clojure.lang.LazySeq clojure.lang.LazySeq clojure.lang.LazySeq)
;; Recursively eager evaluation.
user=> (type (doall* (map (fn [x] (repeat x :foo)) [1 2 3])))
clojure.lang.ChunkedCons
user=> (map type (doall* (map (fn [x] (repeat x :foo)) [1 2 3])))
(clojure.lang.Cons clojure.lang.Cons clojure.lang.Cons)
You can omit the seq call in doall* if all you want is bindings
capture/eager evaluation, and don't actually mind that it's LazySeq
implementing ISeq rather than a 'concrete' sequence.