0

I was looking at the source for the memoize. Coming from languages like C++/Python, this part hit me hard: (let [mem (atom {})] (fn [& args] (if-let [e (find @mem args)] ...

I realize that memoize returns a function, but for storing state, it uses a local "variable" mem. But after memoize returns the function, shouldn't that outer let vanish from scope. How can the function still refer to the mem.

Why doesn't Clojure delete that outer variable, and how does it manage variable names. Like suppose, I make another memoized function, then memoize uses another mem. Doesn't that name clash with the earlier mem?

P.S.: I was thinking that there must be something much be happening in there, that prevents that, so I wrote myself a easier version, that goes like http://ideone.com/VZLsJp , but that still works like the memoize.

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
RandomStudent
  • 417
  • 3
  • 12

1 Answers1

5

Objects are garbage collectable if no thread can access them, as per usual for JVM languages. If a thread has a reference to the function returned by memoize and the function has a reference to the atom in mem then transitively the atom is still accessible.

But after memoize returns the function, shouldn't that outer let vanish from scope. How can the function still refer to the mem.

This is what is called a closure. If a function is defined using a name from its environment, it keeps a reference to that value afterwards - even if the defining environment is gone and the function is the only thing that has access any more.

Like suppose, I make another memoized function, then memoize uses another mem. Doesn't that name clash with the earlier mem?

No, except possibly by confusing programmers. Having multiple scopes each declare their own name mem is very much possible and the usual rules of lexical scoping are used to determine which is meant when mem is read. There are some trickier edge cases such as

(let[foo 2]
  (let[foo (fn[] foo)] ;; In the function definition, foo has the value from the outer scope
    ;; because the second let has not yet bound the name
    (foo))) ;; => 2. 

but generally the idea is pretty simple - the value of a name is the one given in the definition closest in the program text to the place it is used - either in the local scope or in the closest outer scope. Different invocations of memoize create different closures so that the name mem refers to different atoms in each returned function.

Magos
  • 3,004
  • 1
  • 20
  • 20