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