Unexpected binding behavior
Hi all. Is this a bug or a feature? When I bind the var *num* to a new value, in one case it appears the lambda uses the original value, not the newly bound one. (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 --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~--~~~~--~~--~--~---
Re: Unexpected binding behavior
Lazy evaluation is a harsh mistress. user=> (def b1 (binding [*num* 1024] (f1))) #'user/b1 user=> (def b2 (binding [*num* 1024] (f1))) #'user/b2 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. Cheers, Stuart > Hi all. Is this a bug or a feature? When I bind the var *num* to a new > value, in one case it appears the lambda uses the original value, not > the newly bound one. > > (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 > > > --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~--~~~~--~~--~--~---
Re: Unexpected binding behavior
Hi On Tue, Jan 20, 2009 at 7:01 AM, Stuart Halloway 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 --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~--~~~~--~~--~--~---
Re: Unexpected binding behavior
> How would one go about fixing f1 (or b1)? Depends what you want to achieve... here are two possible 'fixes': ; don't use lazy evaluation (defn f1 [] (doall (map (fn [x] *num* ) [1]))) ; use lazy evaluation, but preserve the binding when the lazy sequence is created (defn f1 [] (let [mynum *num*] (map (fn [x] mynum) [1]))) --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~--~~~~--~~--~--~---
Re: Unexpected binding behavior
Doh. Disregard my code example. :-) The reasoning is correct however. Stuart > Hi > > On Tue, Jan 20, 2009 at 7:01 AM, Stuart Halloway > 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 > > > --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~--~~~~--~~--~--~---
Re: Unexpected binding behavior
On Mon, Jan 19, 2009 at 11:01 PM, Stuart Halloway 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 > 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. > OK, good, thanks for the explanation. Is it desirable behavior? Is there some case where a programmer wants this behavior? It's pretty scary to have to consider these hidden effects. Sort of the opposite of FP. My gut tells me I want to get the same results evaluating lazily or eagerly, is that reasonable? Doesn't seem practical to use bindings if it can bite you like this. BTW in the actual case that bit me, a function inside another anonymous function, all inside a macro, used the stale value of the Var. IOW it wasn't a case of evaluating at the repl. The f1 I posted here was an attempt to simplify that case, but it's maybe too simple to demonstrate the full problem. Hugh > Cheers, > Stuart > >> Hi all. Is this a bug or a feature? When I bind the var *num* to a new >> value, in one case it appears the lambda uses the original value, not >> the newly bound one. >> >> (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 >> >> > > > > > > --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~--~~~~--~~--~--~---
Re: Unexpected binding behavior
> OK, good, thanks for the explanation. > > Is it desirable behavior? Is there some case where a programmer wants > this behavior? > > It's pretty scary to have to consider these hidden effects. Sort of > the opposite of FP. 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 --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~--~~~~--~~--~--~---
Re: Unexpected binding behavior
On Tue, Jan 20, 2009 at 11:45 AM, Stuart Halloway wrote: > >> OK, good, thanks for the explanation. >> >> Is it desirable behavior? Is there some case where a programmer wants >> this behavior? >> >> It's pretty scary to have to consider these hidden effects. Sort of >> the opposite of FP. > > If you write functions that work differently depending on a binding, > then you are outside FP anyway. It only works differently depending on a binding under lazy evaluation. Inside FP, outside FP, all I want is no surprises. Hugh --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~--~~~~--~~--~--~---
Re: Unexpected binding behavior
On Jan 20, 10:06 am, "Hugh Winkler" wrote: > Inside FP, outside FP, all I want is no surprises. I agree, this is confusing for new users. Especially given the counter-intuitive (for a new person) way the REPL forces evaluation of everything, making everything seem to be eager when it's not. This aspect of Clojure is very different from most languages, including other Lisps, and the kinds of errors it can produce are often silent and hard to figure out. Some documentation loudly proclaiming the laziness of map and friends, the importance of dorun/doall/doseq, and the potential pitfalls of laziness would be helpful. Perhaps as an FAQ entry if nothing else? --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~--~~~~--~~--~--~---
Re: Unexpected binding behavior
On Tue, Jan 20, 2009 at 12:53 AM, Timothy Pratley wrote: > > >> How would one go about fixing f1 (or b1)? > > Depends what you want to achieve... here are two possible 'fixes': > > ; don't use lazy evaluation > (defn f1 [] > (doall (map (fn [x] *num* ) [1]))) > > ; use lazy evaluation, but preserve the binding when the lazy sequence > is created > (defn f1 [] > (let [mynum *num*] >(map (fn [x] mynum) [1]))) > > 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 --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~--~~~~--~~--~--~---
Re: Unexpected binding behavior
Hi Hugh, I don't see how this would work in general, which is why a I suggested a special-purpose macro before. Surely you would not want a binding to force all sequences while that binding is in effect. And if not that, what would the general strategy be for deciding which sequences to force, and which not? Stuart > On Tue, Jan 20, 2009 at 12:53 AM, Timothy Pratley > wrote: >> >> >>> How would one go about fixing f1 (or b1)? >> >> Depends what you want to achieve... here are two possible 'fixes': >> >> ; don't use lazy evaluation >> (defn f1 [] >> (doall (map (fn [x] *num* ) [1]))) >> >> ; use lazy evaluation, but preserve the binding when the lazy >> sequence >> is created >> (defn f1 [] >> (let [mynum *num*] >> (map (fn [x] mynum) [1]))) >> >> > > 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 > > > --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~--~~~~--~~--~--~---
Re: Unexpected binding behavior
On Tue, Jan 20, 2009 at 3:06 PM, Stuart Halloway wrote: > > Hi Hugh, > > I don't see how this would work in general, which is why a I suggested > a special-purpose macro before. Surely you would not want a binding to > force all sequences while that binding is in effect. And if not that, > what would the general strategy be for deciding which sequences to > force, and which not? OK, well keeping in mind I'm still learning: I agree a macro would do it, approximately this: (defmacro safe-binding [bindings & body] `(binding ~bindings (doall ~...@body))) but why not make "safe" the default? i.e. use "unsafe-binding" if you know it's OK. The doall just forces the result of the function you're calling, right? You said "Surely you would not want a binding to force all sequences while that binding is in effect." No, I would not. You only need to do wrap the body of the binding call in the doall, right? Assuming the general case : callers do not know whether callees have programmed safely for lazy evaluation + binding, and callees do not know whether callers intend to rebind. It would be costly to program every public function as if the caller might rebind some var you are using, so how to write safe code? Caller has to read the documentation and decide to call the function safely (with doall) or not -- that's how it works now. I am suggesting the safe route is for binding to force doall by default; that way if you don't read documentation, your program will work anyway. Again, is the cost of the doall so high, compared to incorrect programs? We're only doing it at the outermost level of the binding. Thanks, Hugh > > Stuart > >> On Tue, Jan 20, 2009 at 12:53 AM, Timothy Pratley >> wrote: >>> >>> How would one go about fixing f1 (or b1)? >>> >>> Depends what you want to achieve... here are two possible 'fixes': >>> >>> ; don't use lazy evaluation >>> (defn f1 [] >>> (doall (map (fn [x] *num* ) [1]))) >>> >>> ; use lazy evaluation, but preserve the binding when the lazy >>> sequence >>> is created >>> (defn f1 [] >>> (let [mynum *num*] >>> (map (fn [x] mynum) [1]))) >>> >>> >> >> 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 >> >> > > > > > > -- Hugh Winkler, CEO Wellstorm Development 31900 Ranch Road 12 Suite 206 Dripping Springs, TX 78620 USA http://www.wellstorm.com/ +1 512 264 3998 x801 --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~--~~~~--~~--~--~---
Re: Unexpected binding behavior
On Tue, Jan 20, 2009 at 3:54 PM, Hugh Winkler wrote: > On Tue, Jan 20, 2009 at 3:06 PM, Stuart Halloway > wrote: >> >> Hi Hugh, >> >> I don't see how this would work in general, which is why a I suggested >> a special-purpose macro before. Surely you would not want a binding to >> force all sequences while that binding is in effect. And if not that, >> what would the general strategy be for deciding which sequences to >> force, and which not? > > > OK, well keeping in mind I'm still learning: > > I agree a macro would do it, approximately this: > > (defmacro safe-binding [bindings & body] >`(binding ~bindings (doall ~...@body))) > > > but why not make "safe" the default? i.e. use "unsafe-binding" if > you know it's OK. > > The doall just forces the result of the function you're calling, > right? You said "Surely you would not want a binding to force all > sequences while that binding is in effect." No, I would not. You only > need to do wrap the body of the binding call in the doall, right? > 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 --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~--~~~~--~~--~--~---
Re: Unexpected binding behavior
That's simpler than the example I was typing up, so I will stop now. :-) Another problem is that you might want the binding to be in effect while defining a sequence, but not while realizing it. Haven't come up with any realistic examples of that, though. Cheers, Stuart > 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 > > > --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~--~~~~--~~--~--~---
Re: Unexpected binding behavior
On Tue, Jan 20, 2009 at 10:54 PM, Hugh Winkler wrote: > but why not make "safe" the default? i.e. use "unsafe-binding" if > you know it's OK. 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. --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~--~~~~--~~--~--~---