Understanding Clojure Closures

42 views
Skip to first unread message

mbrodersen

unread,
Nov 11, 2009, 8:17:05 AM11/11/09
to Clojure
A quick question about how closures work in Clojure.

In this simple recursive expression:

(def t (fn [x] (if (zero? x) 0 (+ x (t (dec x))))))

The fn special form is evaluated within a context where t is not yet
bound.

t is only bound AFTER fn has captured its environment.

In other words, the closure captured by fn doesn't know anything about
t (or maybe only that t is unbound).

And yet it still works if I call t:

(t 5) => 15

My question is how Clojure ensures that t is bound to the closure
after the closure has already been captured?

Thanks
Morten

Alex Osborne

unread,
Nov 11, 2009, 8:34:11 AM11/11/09
to clo...@googlegroups.com
mbrodersen wrote:
> In this simple recursive expression:
>
> (def t (fn [x] (if (zero? x) 0 (+ x (t (dec x))))))
>
> The fn special form is evaluated within a context where t is not yet
> bound.
>
> t is only bound AFTER fn has captured its environment.
>
> In other words, the closure captured by fn doesn't know anything about
> t (or maybe only that t is unbound).
>
> And yet it still works if I call t:
>
> (t 5) => 15
>
> My question is how Clojure ensures that t is bound to the closure
> after the closure has already been captured?

t is a var. The var is created before the body of def is evaluated. At
that point it is unbound. The function closes over the var not the
value of the binding. That's why you can re-evaluate a function,
changing it's behaviour, without having to re-evaluate everything that
calls it.

You'll notice that with locals (which aren't vars, they're immutable) it
doesn't work:

(let [t (fn [x] (if (zero? x) 0 (+ x (t (dec x)))))] (t 2))

Also if you try to use the var in the bindings you'll get an unbound
error, not an unable to resolve symbol error:

(def foo foo)


Meikel Brandmeyer

unread,
Nov 11, 2009, 8:57:46 AM11/11/09
to Clojure
Hi,

On Nov 11, 2:34 pm, Alex Osborne <a...@meshy.org> wrote:

> (let [t (fn [x] (if (zero? x) 0 (+ x (t (dec x)))))] (t 2))

But also note, that you can give an anonymous function a name. %)

(let [t (fn t [x] (if (zero? x) 0 (+ x (t (dec x)))))] (t 2))

Sincerely
Meikel

Howard Lewis Ship

unread,
Nov 11, 2009, 10:58:24 AM11/11/09
to clo...@googlegroups.com
Symbols are late resolved to functions.

(def t (fn ...)) means define a Var bound to symbol t, and store the
function in it. In JVM terms, the function becomes a new class that is
instantiated.

(t (dec x)) means locate the Var bound to symbol t -- at execution
time (not compilation time) --- de-reference the function object
stored there and invoke it.

In fact, t can be temporarily rebound (using the binding macro) or
even completely replaced (using alter-var-root!) at any time. The Var
remains the same but the contents of the Var are changed.

Because of how Clojure is structured, the Var object need only be
resolved from the symbol once (in the generated Java bytecode, the Var
appears as a static final field).
> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clo...@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+u...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en



--
Howard M. Lewis Ship

Creator of Apache Tapestry

The source for Tapestry training, mentoring and support. Contact me to
learn how I can get you up and productive in Tapestry fast!

(971) 678-5210
http://howardlewisship.com

mbrodersen

unread,
Nov 11, 2009, 6:03:23 PM11/11/09
to Clojure
Great answer Alex. Thanks!

Morten

mbrodersen

unread,
Nov 11, 2009, 6:04:30 PM11/11/09
to Clojure
Thanks Howard. Another great answer.

Morten
Reply all
Reply to author
Forward
0 new messages