(def *num* 16)
(defn f1 []
( map (fn [x] *num* ) [1]))
(defn f2 [] ;; same as f1 but calls first on the map
(first
( map (fn [x] *num* ) [1])))
user> (f1)
(16) <--ok
user> (f2)
16 <--ok
user> (binding [*num* 1024] (f1))
(16) <-- expected 1024
user> (binding [*num* 1024] (f2))
1024 <--ok after
all, in spite of the fact the above gave list (16)
Thanks,
Hugh
On Tue, Jan 20, 2009 at 7:01 AM, Stuart Halloway
<stuart....@gmail.com> wrote:
>
> Lazy evaluation is a harsh mistress.
>
> user=> (def b1 (binding [*num* 1024] (f1)))
> #'user/b1
> user=> (def b2 (binding [*num* 1024] (f1)))
> #'user/b2
Did you mean b1 and b2 to have the same definition? If so, I don't
understand what you are trying to demonstrate by doing that.
> user=> b1
> (16)
> user=> b2
> (16)
>
> The difference between the example above and your example is the
> interaction with the REPL. Your f1 is lazy, and is not realized until
> outside the binding, when the REPL needs it. Your f2, also lazy, is
> realized inside the binding because of the call to first.
I thought this stuff was supposed to be easier to reason about! :)
Would it perhaps make sense to save the environment that f1 was called
in and use that during the realisation of its result? That would seem
to be far more sensible, but maybe there's a reason it would not work.
How would one go about fixing f1 (or b1)?
--
Michael Wood <esio...@gmail.com>
If you write functions that work differently depending on a binding,
then you are outside FP anyway. And at that point laziness may no
longer be your friend. You will need to make sure things happen eagerly.
There are probably some opportunities to generalize this with a macro.
Stuart
It only works differently depending on a binding under lazy evaluation.
Inside FP, outside FP, all I want is no surprises.
Hugh
Yes, these work. They presume the author of f1 knows that the caller
is liable to rebind *num*.
Is it always going to be unsafe to use Vars in a lazily evaluated
function? If so, could the compiler or runtime automate forcing doall
or let?
I'm understanding better. The caller can also do
(binding [*num* 1024] (doall (f1)))
The caller doesn't necessarily know he's getting a lazy sequence back,
but this would be a boilerplate pattern you use anytime you use
binding. Again, could/should Clojure automate doing that?
Thanks all for the insights.
Hugh
Uh oh, I see what you mean Stuart. I made f1 return a list of lists:
(defn f1 []
( map (fn [x]
(map (fn [y] *num*) [x])) [1]))
user> (safe-binding [*num* 1024] (f1))
((16))
OK, so much for the magic bullet.
Hugh
Not all seqs are finite. Also, Clojure skews toward lazy-by-default.
Once a function has run inside a `binding, I would assume that the
result it delivers is exactly what it means to deliver. And `map means
to deliver a lazy seq.
If `binding, however, decided to do something with that result before
returning, then I would consider that a side-effect of binding.
It would mean that I cannot use `binding that produce a lazy-seq
sources from some IO resource or any infinite seq. This "safe" would
probably either do too much work in the wrong thread or blow my heap.
From the interaction bellow, I'd probably prefer the style of the 'h
function for this kind of situation:
user=> (def *num* 16)
#'user/*num*
user=> (defn f [] (map (fn [_] *num*) [1]))
#'user/f
user=> (defn g [] (map (constantly *num*) [1]))
#'user/g
user=> (defn h [n] (map (constantly n) [1]))
#'user/h
user=> (binding [*num* 1024] (f))
(16)
user=> (binding [*num* 1024] (g))
(1024)
user=> (binding [*num* 1024] (h *num*))
(1024)
--
Venlig hilsen / Kind regards,
Christian Vest Hansen.