newbie question on binding

1 view
Skip to first unread message

wubbie

unread,
Jan 5, 2009, 12:35:09 PM1/5/09
to Clojure
Hi,
This example is from clojure site.
My question is on line 5 and line 6:
The confusion is str is a function and here looks like used as a
regular variable.

Thanks in advance.
Sun


(defn loves [x y]
(str x " loves " y))
(defn test-rebind []
(println (loves "ricky" "lucy"))
(let [str-orig str] ---> line 5:
(binding [str (fn [& args] --> line 6:
(println "Logging str")
(apply str-orig args))] -->line 8:
(println (loves "fred" "ethel")))))
(test-rebind)

ricky loves lucy
Logging str
Logging str
Logging str
Logging str
Logging str
fred loves ethel

Randall R Schulz

unread,
Jan 5, 2009, 1:02:05 PM1/5/09
to clo...@googlegroups.com
On Monday 05 January 2009 09:35, wubbie wrote:
> Hi,
> This example is from clojure site.
> My question is on line 5 and line 6:
> The confusion is str is a function and here looks like used as a
> regular variable.
>
> Thanks in advance.
> Sun
>
>
> (defn loves [x y]
> (str x " loves " y))
> (defn test-rebind []
> (println (loves "ricky" "lucy"))
> (let [str-orig str] ---> line 5:
> (binding [str (fn [& args] --> line 6:
> (println "Logging str")
> (apply str-orig args))] -->line 8:
> (println (loves "fred" "ethel")))))
> (test-rebind)

The value held by the global Var named "str" is saved in str-orig. Then
inside the let that binds str-orig, a local override of that global
binding is established that essentially, within the scope of that
(binding ...) form, redefines str. This is a dynamically scoped binding
in the classic sense that when code that invokes (str ...) is called
somewhere "beneath" or "inside" this local (binding ...), it will get
the logging form of (str), which, after printing its logging output,
defers to the original str function via the value saved in str-orig.
Other calls to (str ...) (in other threads), even if they occur
concurrent to the execution of loves will not be affected by this local
binding.

Neat, eh?


> ricky loves lucy
> Logging str
> Logging str
> Logging str
> Logging str
> Logging str
> fred loves ethel


Randall Schulz

Christian Vest Hansen

unread,
Jan 5, 2009, 1:02:26 PM1/5/09
to clo...@googlegroups.com
On Mon, Jan 5, 2009 at 6:35 PM, wubbie <sun...@gmail.com> wrote:
>
> Hi,
> This example is from clojure site.
> My question is on line 5 and line 6:
> The confusion is str is a function and here looks like used as a
> regular variable.

The var that is clojure.core/str aka. 'str, points to a function that
implements it. And vars, any vars, can be shadowed/rebound
temporarily, and this is what 'binding does.

>
> Thanks in advance.
> Sun
>
>
> (defn loves [x y]
> (str x " loves " y))
> (defn test-rebind []
> (println (loves "ricky" "lucy"))
> (let [str-orig str] ---> line 5:
> (binding [str (fn [& args] --> line 6:
> (println "Logging str")
> (apply str-orig args))] -->line 8:
> (println (loves "fred" "ethel")))))
> (test-rebind)
>
> ricky loves lucy
> Logging str
> Logging str
> Logging str
> Logging str
> Logging str
> fred loves ethel
>
> >
>



--
Venlig hilsen / Kind regards,
Christian Vest Hansen.

Meikel Brandmeyer

unread,
Jan 5, 2009, 2:14:51 PM1/5/09
to clo...@googlegroups.com
Hi,

Am 05.01.2009 um 18:35 schrieb wubbie:

> This example is from clojure site.
> My question is on line 5 and line 6:
> The confusion is str is a function and here looks like used as a
> regular variable.
>
> Thanks in advance.

binding can be used to temporarily assign
a different value to a global Var.

Let's understand the example line by line.

> (defn loves [x y]
> (str x " loves " y))

First we define a function "loves" which uses
the standard str function to concate it's
arguments. So far easy to understand.

> (defn test-rebind []
> (println (loves "ricky" "lucy"))

Here we do a proof of concept and show
that our function "loves" works as expected.

Output:
> ricky loves lucy


Now to nitty-gritty:

> (let [str-orig str] ---> line 5:

First we save the original str function in
a local.

> (binding [str (fn [& args] --> line 6:
> (println "Logging str")
> (apply str-orig args))] -->line 8:

Now we use binding to replace the global
str function with another one, which we
define using the original str function. We
log, that we called our modified function
and the just delegate all arguments to the
original str function, which we saved in
str-orig.

This modified str is only available inside
the body of the binding form. We leave
it by reaching the end of the body or throwing
an exception, the original str is again
restored. Also other threads also don't see
the modified str. This is really restricted
to the body of the binding.

Now we call again our initial function "loves".
Please note: we did not modify our function
in any way.

> (println (loves "fred" "ethel")))))

See the output:

> Logging str
> Logging str
> Logging str
> Logging str
> Logging str
> fred loves ethel

Huh? So although we did not modify our "loves"
function we get a different output? Why is that.
The solution is binding. The loves function is
called *inside* the binding body and although
it is defined *outside* the binding, it will use the
modified str function.

So you are right the binding looks similar to
a let.

(binding [str ...]
...)

vs.

(let [str ...]
...)

However the let introduces a new "str" which
*shadows* the global str function and is only
visible for the code in the let. In particular
functions called inside the let will see the
str which was in effect when they were defined.

The binding however *replaces* the str which
was in effect, when the function was defined.
Of course this works only with global Vars and
not with locals as introduced by let.

(Note: the number of "Logging"s depends on
the implementation of the str function.)

Hope this helps.

Sincerely
Meikel

wubbie

unread,
Jan 5, 2009, 5:00:59 PM1/5/09
to Clojure
Hi,
Why are there multiple "Logging str" output.
Also in (apply str-orig args), I don't see any args passed at all!

Thanks
sun
> smime.p7s
> 5KViewDownload

Michael Wood

unread,
Jan 12, 2009, 9:37:26 AM1/12/09
to clo...@googlegroups.com
On Tue, Jan 6, 2009 at 12:00 AM, wubbie <sun...@gmail.com> wrote:
>
> Hi,
> Why are there multiple "Logging str" output.

Because str calls itself recursively.

> Also in (apply str-orig args), I don't see any args passed at all!

I'm not sure what you mean here. The "args" in (apply str-orig args)
is the list of arguments that are passed to str-orig.

--
Michael Wood <esio...@gmail.com>

Meikel Brandmeyer

unread,
Jan 12, 2009, 12:52:48 PM1/12/09
to clo...@googlegroups.com
Hi,

I'm sorry. I wanted to answer you but it somehow got lost.

Am 05.01.2009 um 23:00 schrieb wubbie:

> Why are there multiple "Logging str" output.

As Michael already said, it's because the originial str
calls itself recursively.

Here is a thread, which explains this in more detail.

http://groups.google.com/group/clojure/tree/browse_frm/thread/63ff934f1a2d4afd/0e97295c76f057e3?rnum=1&q=binding+str+logging&_done=%2Fgroup%2Fclojure%2Fbrowse_frm%2Fthread%2F63ff934f1a2d4afd%2F0e97295c76f057e3%3Flnk%3Dgst%26q%3Dbinding%2Bstr%2Blogging%26#doc_4e855da814b45e0b

> Also in (apply str-orig args), I don't see any args passed at all!

Again: as Michael already said, the list of arguments to str-orig
is contained in args.

(apply str-orig [:a :b :c]) <=> (str-orig :a :b :c)

Only the last argument to str-orig must be a sequable thing, like
a vector or a list. Any arguments given directly are inserted before
the arguments form the list.

(apply str-orig 1 2 [:a :b :c]) <=> (str-orig 1 2 :a :b :c)

Sincerely
Meikel


Reply all
Reply to author
Forward
0 new messages