Unexpected binding behavior

2009-01-19 Thread Hugh Winkler

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

2009-01-19 Thread Stuart Halloway

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

2009-01-19 Thread Michael Wood

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

2009-01-19 Thread Timothy Pratley


> 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

2009-01-20 Thread Stuart Halloway

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

2009-01-20 Thread Hugh Winkler

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

2009-01-20 Thread Stuart Halloway

> 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

2009-01-20 Thread Hugh Winkler

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

2009-01-20 Thread Brian Carper

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

2009-01-20 Thread Hugh Winkler

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

2009-01-20 Thread Stuart Halloway

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

2009-01-20 Thread Hugh Winkler

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

2009-01-20 Thread Hugh Winkler

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

2009-01-20 Thread Stuart Halloway

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

2009-01-20 Thread Christian Vest Hansen

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
-~--~~~~--~~--~--~---