Re: Clojure Scoping Rules

2009-11-24 Thread Mark Engelberg
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

2009-11-24 Thread Garth Sheldon-Coulson
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

2009-11-24 Thread Meikel Brandmeyer

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

2009-11-23 Thread Meikel Brandmeyer

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

2009-11-23 Thread Chouser
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

2009-11-23 Thread Meikel Brandmeyer
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

2009-11-23 Thread Meikel Brandmeyer
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

2009-11-23 Thread Meikel Brandmeyer

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

2009-11-23 Thread André Ferreira
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

2009-11-23 Thread Mark Engelberg
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

2009-11-23 Thread Garth Sheldon-Coulson
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

2009-11-22 Thread nchubrich
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

2009-11-22 Thread Richard Newman
 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

2009-11-22 Thread nchubrich
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

2009-11-22 Thread Richard Newman
 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

2009-11-22 Thread nchubrich
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

2009-11-22 Thread Richard Newman
 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

2009-11-21 Thread Armando Blancas
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

2009-11-21 Thread Mark Engelberg
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

2009-11-21 Thread Graham Fawcett
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

2009-11-21 Thread nchubrich
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

2009-11-21 Thread Richard Newman
 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

2009-11-21 Thread ataggart
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

2009-11-21 Thread cody koeninger
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

2009-11-20 Thread kunjaan
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

2009-11-20 Thread Mark Engelberg
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

2009-11-20 Thread Mark Engelberg
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