Re: Clojure Scoping Rules
On Mon, Nov 23, 2009 at 11:39 PM, Garth Sheldon-Coulson g...@mit.edu wrote: Hi Mark, In Clojuratica I make what I think is good, clean, compelling use of dynamic vars. I rewrote the code to use dynamic vars after I found that doing it the other way became unwieldy and inelegant. OK this makes sense to me. Your use case is actually very similar to mine (lots of interrelated functions where it would be cleaner to have them refer to some sort of global variable that is set prior to execution). But I got tripped up because: 1. I think I had more use of laziness in my code than how you describe yours. 2. I didn't know the push-thread-bindings/pop-thread-bindings trick to fix the spots with laziness (I tried just forcing the lazy sequences which killed my performance -- I really needed laziness). 3. Even if I had known the trick, I may have had difficulty identifying all the spots where laziness was an issue since I was returning a complex lazy tree structure. (I probably would have tried, though.) So now that you've explained your use of dynamic bindings, I can totally imagine scenarios where it is easier to tame the laziness than go back to passing all the globals around, especially with the convenient bound-seq macro that Meikel posted. This brings up another question in my mind. What kind of impact would it have on people's code if *all* calls to lazy-seq operated the way that bound-seq does? Would this make things more intuitive or less intuitive? Would this radically reduce performance? -- 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 Note that posts from new members are moderated - please be patient with your first post. 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: Clojure Scoping Rules
Happy it helped. I should mention that I used Meikel's docs as a guide, but my code don't actually push or pop all the thread bindings every time I return a lazy seq or a fn. It felt a little ugly to me to bind *all* the dynamic vars in the namespace when I knew there were only two I needed to care about. So, in the locations where I build lazy seqs or fns to return to the user, I manually close over and bind *options* and *kernel*. I'd be really interested in hearing others' views on the propriety of binding all the dynamic vars every time using bound-fn or equivalent. On 11/24/09, Mark Engelberg mark.engelb...@gmail.com wrote: On Mon, Nov 23, 2009 at 11:39 PM, Garth Sheldon-Coulson g...@mit.edu wrote: Hi Mark, In Clojuratica I make what I think is good, clean, compelling use of dynamic vars. I rewrote the code to use dynamic vars after I found that doing it the other way became unwieldy and inelegant. OK this makes sense to me. Your use case is actually very similar to mine (lots of interrelated functions where it would be cleaner to have them refer to some sort of global variable that is set prior to execution). But I got tripped up because: 1. I think I had more use of laziness in my code than how you describe yours. 2. I didn't know the push-thread-bindings/pop-thread-bindings trick to fix the spots with laziness (I tried just forcing the lazy sequences which killed my performance -- I really needed laziness). 3. Even if I had known the trick, I may have had difficulty identifying all the spots where laziness was an issue since I was returning a complex lazy tree structure. (I probably would have tried, though.) So now that you've explained your use of dynamic bindings, I can totally imagine scenarios where it is easier to tame the laziness than go back to passing all the globals around, especially with the convenient bound-seq macro that Meikel posted. This brings up another question in my mind. What kind of impact would it have on people's code if *all* calls to lazy-seq operated the way that bound-seq does? Would this make things more intuitive or less intuitive? Would this radically reduce performance? -- 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 Note that posts from new members are moderated - please be patient with your first post. 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 -- Sent from my mobile device -- 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 Note that posts from new members are moderated - please be patient with your first post. 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: Clojure Scoping Rules
Hi, Am 24.11.2009 um 18:47 schrieb Garth Sheldon-Coulson: I'd be really interested in hearing others' views on the propriety of binding all the dynamic vars every time using bound-fn or equivalent. I asked whether it should to take a map or not in the assembla thread of the ticket. But there was no response on the question. So I guess it is ok. It really only considers vars with a non-root binding. So unless you bind thousand different Vars it's likely not a problem. You wouldn't use binding inside a tight loop anyway... Sincerely Meikel smime.p7s Description: S/MIME cryptographic signature
Re: Clojure Scoping Rules
Hi, Am 21.11.2009 um 05:22 schrieb Mark Engelberg: Which reminds me, every once in a while I see people talking about this here, and brainstorming up some alternatives to binding that might interact better with lazy data structures. Has there been any real progress on this, or has every proposed solution been equally problematic? I wrote up a little blog post on the problem and the possible solutions. Feedback welcome. http://kotka.de/blog/clojure/Taming_the_Bound_Seq.html Sincerely Meikel smime.p7s Description: S/MIME cryptographic signature
Re: Clojure Scoping Rules
On Sat, Nov 21, 2009 at 4:37 PM, Meikel Brandmeyer m...@kotka.de wrote: Hi, Am 21.11.2009 um 05:22 schrieb Mark Engelberg: Which reminds me, every once in a while I see people talking about this here, and brainstorming up some alternatives to binding that might interact better with lazy data structures. Has there been any real progress on this, or has every proposed solution been equally problematic? I wrote up a little blog post on the problem and the possible solutions. Feedback welcome. http://kotka.de/blog/clojure/Taming_the_Bound_Seq.html That's excellent Meikel, thanks. Any reson you didn't use the with-bindings macro to make your final example a bit simpler? --Chouser -- 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 Note that posts from new members are moderated - please be patient with your first post. 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: Clojure Scoping Rules
Hi, On Nov 23, 3:36 pm, Chouser chou...@gmail.com wrote: http://kotka.de/blog/clojure/Taming_the_Bound_Seq.html That's excellent Meikel, thanks. Any reson you didn't use the with-bindings macro to make your final example a bit simpler? Woops. The reason might be the time of day (23 o'clock after getting up on 5:30 in the morning). I simply forgot about with-bindings. :( I will update the post tonight. Maybe I'll also show a more detailed example for bound-fn. Sincerely Meikel -- 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 Note that posts from new members are moderated - please be patient with your first post. 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: Clojure Scoping Rules
Hi, On Nov 23, 3:32 pm, nchubrich nicholas.chubr...@gmail.com wrote: Meikel, is get-thread-bindings only in a development version of Clojure? I have 1.09.11 and don't see it documented or usable (or in the online docs). I'm not sure what 1.09.11 means, but the following commits added push-/ pop-/get-thread-bindings as wells as with-bindings and bound-fn. thread-bindings interface: http://github.com/richhickey/clojure/commit/110b9c2eb8a128d837e6e620efc7e1c4e33feb82 with-bindings and bound-fn: http://github.com/richhickey/clojure/commit/fbacc4a5751fa5c15baa599b5a058cd81b05a247 Sincerely Meikel -- 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 Note that posts from new members are moderated - please be patient with your first post. 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: Clojure Scoping Rules
Chris, Graham, Am 23.11.2009 um 21:21 schrieb Graham Fawcett: Very nice. A generalized version might be more useful to your readers: take an input seq, and return an output seq which is evaluated stepwise in the binding environment. Thank you for your good comments. I updated the post with a bound-seq helper, which turns any given lazy input sequence into a bound sequence. The environment might be chosen manually in the style of binding. Or alternatively get-thread-bindings captures the whole environment. http://kotka.de/blog/clojure/Taming_the_Bound_Seq.html Sincerely Meikel smime.p7s Description: S/MIME cryptographic signature
Re: Clojure Scoping Rules
Would it be possible to create an implementation of delay and lazy-seq that didn't use fn to delay evaluation, or atleast captured dynamic variables? (delay (+ x 3)) reasonable semantics in current clojure (let [x x] (delay (+ x 3))) (delay (fn [y] (+ x y))) semantics should be the same it already is (x should get it's value from the dynamic binding) (delay [x (fn [y] (+ x y))]) semantics should be (let [G_N x] (delay [G_N (fn [y] (+ x y))])), so all captured variables would need renaming using gensyms. delay would need it's body macroexpanded code to be able to correctly capture variables. It would also try to capture local variables, but that wouldn't change the semantics and would probably be optimized away by the compiler. I might have missed some important detail, since dynamic binding and lazyness have such a peculiar semantics interaction, but it would yield much more reasoanable execution, wouldn't it? -- 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 Note that posts from new members are moderated - please be patient with your first post. 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: Clojure Scoping Rules
Meikel's blog post quotes: running into a lot of such trouble is a sign, that you misuse dynamic variables. Use them wisely. I'd like to see examples of what you think is a good, clean, compelling use of dynamic variables that are properly used wisely. My own experience is that if the code is simple enough for you to analyze the use of binding and be sure it is correct, then the code is also simple enough to have easily written in another way (perhaps using explicit parameter passing). On the other hand, if the use of binding is complex enough to really matter, it is also sufficiently complex you can't be 100% sure binding will do what you expect. I even somewhat question the places where Clojure internally uses bindings. For example, if you use with-precision to try to control floating point behavior within a structure that potentially has some deep laziness (like a tree) that can't easily be forced with a doall, you're in for a surprise. I would like to be proven wrong, so I'm serious about wanting to see good examples of dynamic binding. -- 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 Note that posts from new members are moderated - please be patient with your first post. 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: Clojure Scoping Rules
Hi Mark, In Clojuratica I make what I think is good, clean, compelling use of dynamic vars. I rewrote the code to use dynamic vars after I found that doing it the other way became unwieldy and inelegant. To simplify a little, the API consists of just one main function, let's call it math-evaluate. Every time the user calls math-evaluate, he or she can specify any of a number of flags. Just for illustration, let's say two of these flags are :flag1 and :flag2. The math-evaluate function parses the flags and places them in a map called *options* (or options before I was using dynamic vars). It then calls into the core functions of the code base and returns the result. The core functions call one another in complex ways. There are scores of core functions. All of them have need to have access to the options map, if not for their own consumption then for passing to other core functions. Without dynamic vars, every single function in my code base would need to have an options argument. Moreover, every mutual call between these scores of functions would need to include the options map as an argument. I did it this way for a while, and it was painful. Moreover, if in the future I decided that every core function needed access to *two* of these global-within-the-dynamic-scope vars instead of one, I would need to edit the signature of every function and edit every mutual call between the functions. It turned out that this actually happened: I decided I needed another map to store a few I/O bindings (it's called *kernel*). Because I was using dynamic vars, I was able to change just one location in my code---the namespace where the dynamics are interned---instead of several hundred locations. The trade-off is the following. When I need to return a function or lazy seq I need to close over the *options* and *kernel* vars by jumping through the hoops Meikel has documented. There are only five or six such places in the code, so I'm more than happy to do this. It feels quite elegant to me, although I'd like a little more syntactic sugar. Note that the dynamic vars are solely for my convenience as a programmer, not part of the API of the software. I shield them from the user, so if the user tried to bind *options* him- or herself it would have no effect on the code. Every call to math-evaluate binds the dynamic vars anew on the basis of the flags passed. My use of dynamics is an implementation detail. The source is on github. The real-laziness branch is the newest and uses bound lazy-seqs the most, particularly in clojuratica.base.parse. Comments on this usage welcome. Garth On Tue, Nov 24, 2009 at 1:43 AM, Mark Engelberg mark.engelb...@gmail.comwrote: Meikel's blog post quotes: running into a lot of such trouble is a sign, that you misuse dynamic variables. Use them wisely. I'd like to see examples of what you think is a good, clean, compelling use of dynamic variables that are properly used wisely. My own experience is that if the code is simple enough for you to analyze the use of binding and be sure it is correct, then the code is also simple enough to have easily written in another way (perhaps using explicit parameter passing). On the other hand, if the use of binding is complex enough to really matter, it is also sufficiently complex you can't be 100% sure binding will do what you expect. I even somewhat question the places where Clojure internally uses bindings. For example, if you use with-precision to try to control floating point behavior within a structure that potentially has some deep laziness (like a tree) that can't easily be forced with a doall, you're in for a surprise. I would like to be proven wrong, so I'm serious about wanting to see good examples of dynamic binding. -- 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 Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.comclojure%2bunsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -- 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 Note that posts from new members are moderated - please be patient with your first post. 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: Clojure Scoping Rules
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 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. To quote Mark Engleberg: If you want to choose code simplification over laziness, it's not always even clear how to do that safely. For example, it's not uncommon to end up with lazy sequences of lazy sequences [...], and then a single doall isn't going to the trick. Wrapping doall around every single call in your code is unrealistic. I would add to that that casting your types back to what they were is also unrealistic. 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? -- 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 Note that posts from new members are moderated - please be patient with your first post. 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: Clojure Scoping Rules
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. -- 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 Note that posts from new members are moderated - please be patient with your first post. 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: Clojure Scoping Rules
Richard--- What if you accidentally cause a library to realize an infinite lazy sequence? True, that's a problem. But couldn't the library protect itself by putting a (binding [*strict* false] ...) in front of its code? (Having a namespace-level binding construct would be helpful.) This raises a more general thought about binding in general (while we're talking about new language constructs). Right now you have basically two choices for influencing code: Parameter passing, which is, nothing happens to my code without my permission, and dynamic binding, which is You might have the rug yanked from under you, and there's nothing you can do about it! (Exclamation point here is an indicator of mutation, not exclamation.) Well, this might be wildly speculative, but couldn't there be a middle way? I.E. a scope or a function body (if we are trying to unify these concepts then Scope and Function Body is really the same thing), instead of exporting variables of things that can be substituted, basically sets up a negotiation over what can and should be bound. It might state things like binding this such-and-such a way is a \really \bad idea, but if you do X first, it would be OK; and of course if you \insist The actual binding (if such a guard has been put up) then doesn't happen until the conditions have been run through. I'm not really sure what this construct would look like. Somebody else might have a better idea Maybe the problem with binding is not that it's inherently evil, it's just not democratic enough. -- 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 Note that posts from new members are moderated - please be patient with your first post. 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: Clojure Scoping Rules
True, that's a problem. But couldn't the library protect itself by putting a (binding [*strict* false] ...) in front of its code? (Having a namespace-level binding construct would be helpful.) That's exactly what I meant when I wrote it would introduce a problem that library authors have to think about. I don't support any solution that requires libraries to protect themselves from decisions made in user code, particularly when it's so easy to externally control the evaluation of lazy sequences -- just visit them within the correct bindings! This raises a more general thought about binding in general (while we're talking about new language constructs). Right now you have basically two choices for influencing code: Parameter passing, which is, nothing happens to my code without my permission, and dynamic binding, which is You might have the rug yanked from under you, and there's nothing you can do about it! (Exclamation point here is an indicator of mutation, not exclamation.) An interesting point that I don't think anyone's addressed: it's not so much that the rug might be yanked from under you, but that you might find yourself on a different rug when asked to do the work. That can actually be advantageous; you can create lazy sequences and hand them somewhere else, where the appropriate bindings will be enforced during evaluation. (Maybe selecting an output stream or queue dynamically while processing a lazy stream of values, for example). Perhaps I'm just an optimist, or think a little differently. I'm not really sure what this construct would look like. Somebody else might have a better idea I believe that Rich has done some speculative work on scoping constructs. Maybe the problem with binding is not that it's inherently evil, it's just not democratic enough. Heh, I would say it's too democratic — there's something you want to do, but the broader 'society' (the existing configuration of lazy constructs and bindings) wants to do something else, so you must suffer! I'd venture a different opinion: most programmers are not used to thinking of evaluation outside the lexical order (much the same as the switch to concurrent programming). Once you get that, it ceases to be a big deal, and you use appropriate techniques (thunks, controlling evaluation yourself, taking care to choose your scopes, etc.) without much conscious thought. This switch causes a divide in discussions, where one side sees a huge problem to be fixed, and the other side sees an interesting language feature which both requires care and offers power, but arises naturally from the semantics of the language. -- 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 Note that posts from new members are moderated - please be patient with your first post. 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: Clojure Scoping Rules
Richard, do you know where one can read about Rich Hickey's speculative work on scoping constructs? I did find a good description by him of what Clojure currently does, from 2007: http://markmail.org/message/kpuq4dvcavek26sp#query:+page:1+mid:mgfsubipgaqdmzru+state:results -- 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 Note that posts from new members are moderated - please be patient with your first post. 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: Clojure Scoping Rules
Richard, do you know where one can read about Rich Hickey's speculative work on scoping constructs? I did find a good description by him of what Clojure currently does, from 2007: http://clojure.org/todo http://www.assembla.com/spaces/clojure/tickets/2-Scopes Better off asking Rich, I think :) I believe Scopes as written is related to things like with-open more than bindings, but I'm sure there's interplay. -- 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 Note that posts from new members are moderated - please be patient with your first post. 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: Clojure Scoping Rules
I sympathize with your difficulties, but isn't there something fundamentally incompatible between the later-or-never of lazy-seq and the this-way-here-and-now for which dynamic binding is good for? In this case you picked laziness over code simplification, maybe it'll be the other way around some times. I'm not sure if both features belong together. -- 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 Note that posts from new members are moderated - please be patient with your first post. 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: Clojure Scoping Rules
On Sat, Nov 21, 2009 at 8:12 AM, Armando Blancas armando_blan...@yahoo.com wrote: I sympathize with your difficulties, but isn't there something fundamentally incompatible between the later-or-never of lazy-seq and the this-way-here-and-now for which dynamic binding is good for? In this case you picked laziness over code simplification, maybe it'll be the other way around some times. I'm not sure if both features belong together. The point is that many built-in Clojure functions return lazy sequences. If you want to choose code simplification over laziness, it's not always even clear how to do that safely. For example, it's not uncommon to end up with lazy sequences of lazy sequences, for example, and then a single doall isn't going to the trick. Wrapping doall around every single call in your code is unrealistic. So yes, I agree that these features don't coexist well together, but I also contend that it's extremely difficult to eliminate all uses of laziness from complex code. Therefore, binding is a construct that should almost always be avoided, IMO. Intuitively, it seems to me that what one wants is for lazy data structures to contain bound values in some sort of closure-like thing so rebindings after the fact don't affect the data structure, but regular functions should retain their fully-dynamic behavior. I haven't really thought it through, so probably this doesn't quite work, but this kind of separation between the behavior of data structures and functions when dealing with dynamic binding is what's been going through my mind. -- 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 Note that posts from new members are moderated - please be patient with your first post. 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: Clojure Scoping Rules
On Sat, Nov 21, 2009 at 12:17 PM, Mark Engelberg mark.engelb...@gmail.com wrote: Intuitively, it seems to me that what one wants is for lazy data structures to contain bound values in some sort of closure-like thing so rebindings after the fact don't affect the data structure, but regular functions should retain their fully-dynamic behavior. I haven't really thought it through, so probably this doesn't quite work, but this kind of separation between the behavior of data structures and functions when dealing with dynamic binding is what's been going through my mind. In Haskell, you might use a state-carrying monad as the 'closure-like thing'. If you squint enough, those monads look an awful lot like dynamic binding contexts. They have the variable-hiding properties of dynamic binding, but with a scope that's more amenable to lazy evaluation. The same technique ought to work in Clojure. I haven't played at all with the monad tools in clojure.contrib, but you might want to give them a try. If you're new to monads, there are quite a few Haskell tutorials; I won't try to enumerate them. ;-) This link is an overview of the Reader monad, which I think is most similar to the dynamic binding construct: http://www.haskell.org/all_about_monads/html/readermonad.html Best, Graham -- 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 Note that posts from new members are moderated - please be patient with your first post. 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: Clojure Scoping Rules
Regarding Clojure sequence functions: why couldn't they have the option of returning non-lazy seqs? Because you don't always really need lazy seqs. It might be nice to have the option. (map (fn[x] x) [1 2 3] :strict) - [1 2 3] or even (binding [*strict* true] (map (fn[x] x) [1 2 3])) - [1 2 3] -- 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 Note that posts from new members are moderated - please be patient with your first post. 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: Clojure Scoping Rules
It might be nice to have the option. You do -- wrap the form in doall. (binding [*strict* true] (map (fn[x] x) [1 2 3])) would be equivalent to (doall (map identity [1 2 3])) -- 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 Note that posts from new members are moderated - please be patient with your first post. 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: Clojure Scoping Rules
Getting back to the initial post, this would be the (almost) equivalent code in java: public class Test{ static int x = 1; void bindingX(int val){ x = val; } int dummyFn2(){ return x + 1; } void dummyFn(){ System.out.println(entering function: + x); int x = 100; System.out.println(after let: + x); x = dummyFn2(); System.out.println(after let and dummy2: + x); bindingX(100); System.out.println(after binding: + x); x = dummyFn2(); System.out.println(after binding and dummy2: + x); } public static void main(String[] args){ new Test().dummyFn(); } } The confusion lies in poor naming style, conflating local names with global names, and not writing functions in a functional style. On Nov 20, 7:40 pm, kunjaan kunj...@gmail.com wrote: Even though I have used Clojure, I hadn't looked at the scoping rules in detail. I am getting more confused as I read the documentations. I made a small test to try out the scoping resolutions and am apalled at the complexity. Could somebody explain the intent and various rules that Clojure uses? (def x 1) (defn dummy-fn2[] (+ x 1)) (defn dummy-fn[] (println entering function: x) (let [x 100] (println after let: x) (let [x (dummy-fn2)] (println after let and dummy2: x) (binding [x 100] (println after binding: x) (let [x (dummy-fn2)] (println after binding and dummy2: x)) 1:2 foo= (dummy-fn) entering function: 1 after let: 100 after let and dummy2: 2 after binding: 2 after binding and dummy2: 101 nil -- 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 Note that posts from new members are moderated - please be patient with your first post. 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: Clojure Scoping Rules
http://clojure.org/lisps All (global) Vars can be dynamically rebound without interfering with lexical local bindings. No special declarations are necessary to distinguish between dynamic and lexical bindings. Other part of that explanation is whether x in a given piece of code refers to a lexical (local) or dynamic (global) value. It seems that if there's a local value available for x, it always hides the global value for x. Global value is only looked up when there's no local value available in the surrounding text. binding may look like a lexical construct, since it has braces that enclose an area of code, but it's not. It affects code that actually executes during that time, not code that was written in that space. And yeah, dynamic scope interacting with laziness is the single most confusing thing about Clojure, even if you've had a little lisp experience. Use the variants of def just for functions or macros; use ref or let for variables; avoid using binding. -- 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 Note that posts from new members are moderated - please be patient with your first post. 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
Clojure Scoping Rules
Even though I have used Clojure, I hadn't looked at the scoping rules in detail. I am getting more confused as I read the documentations. I made a small test to try out the scoping resolutions and am apalled at the complexity. Could somebody explain the intent and various rules that Clojure uses? (def x 1) (defn dummy-fn2[] (+ x 1)) (defn dummy-fn[] (println entering function: x) (let [x 100] (println after let: x) (let [x (dummy-fn2)] (println after let and dummy2: x) (binding [x 100] (println after binding: x) (let [x (dummy-fn2)] (println after binding and dummy2: x)) 1:2 foo= (dummy-fn) entering function: 1 after let: 100 after let and dummy2: 2 after binding: 2 after binding and dummy2: 101 nil -- 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 Note that posts from new members are moderated - please be patient with your first post. 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: Clojure Scoping Rules
Conceptually, the best way to think about it is that your binding sets the global x to 100 and restores the global x to 1 when exiting the scope of the binding construct. It has no effect on your local x. Dynamic binding is confusing, and should be used with care. If you stick with the lexical scoping of function closures and lets, things should behave the way you expect. --Mark On Fri, Nov 20, 2009 at 7:40 PM, kunjaan kunj...@gmail.com wrote: Even though I have used Clojure, I hadn't looked at the scoping rules in detail. I am getting more confused as I read the documentations. I made a small test to try out the scoping resolutions and am apalled at the complexity. Could somebody explain the intent and various rules that Clojure uses? (def x 1) (defn dummy-fn2[] (+ x 1)) (defn dummy-fn[] (println entering function: x) (let [x 100] (println after let: x) (let [x (dummy-fn2)] (println after let and dummy2: x) (binding [x 100] (println after binding: x) (let [x (dummy-fn2)] (println after binding and dummy2: x)) 1:2 foo= (dummy-fn) entering function: 1 after let: 100 after let and dummy2: 2 after binding: 2 after binding and dummy2: 101 nil -- 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 Note that posts from new members are moderated - please be patient with your first post. 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 -- 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 Note that posts from new members are moderated - please be patient with your first post. 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: Clojure Scoping Rules
On Fri, Nov 20, 2009 at 8:02 PM, Mark Engelberg mark.engelb...@gmail.com wrote: Dynamic binding is confusing, and should be used with care. Elaborating on my previous point: it's especially confusing because dynamic binding interacts poorly with lazy data structures, which is predominantly what you deal with in Clojure. If your lazy data structure depends on something that is dynamically bound using binding, there is a good chance it won't get evaluated until after you exit the scope of the binding and the global variable is restored to its earlier state, so your lazy data structure ends up not using the value that was set with binding. It can get messy fast. There were a couple times I used dynamic binding, and I was really excited about it. I had never used a language with a dynamic binding construct, and it seemed like a perfect fit for what I was doing. I was threading a couple of values through a bunch of mutually recursive functions that generated a tree. The values I was threading controlled certain aspects of the tree generation. I realized that by making these generation parameters global values, and just wrapping binding to set the parameters around the call to my tree generator, I could avoid all these extra inputs on a whole slew of functions that were just passing these parameters around so they'd be available in the functions that really needed them. But my tree generator used laziness in some non-obvious places, and it took me a long time to notice that when I didn't fully evaluate the tree right away, I was getting very different results. It was hard to track down the problem, and ultimately I had to go back to threading all the parameters through the functions explicitly. The experience really turned me off of dynamic binding in Clojure, since it has such a strong laziness focus. Which reminds me, every once in a while I see people talking about this here, and brainstorming up some alternatives to binding that might interact better with lazy data structures. Has there been any real progress on this, or has every proposed solution been equally problematic? -- 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 Note that posts from new members are moderated - please be patient with your first post. 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