> Is there a way to unregister some names from a namespace without reloading it ?
This is a bit trickier than one might think. An example illustrates this;
Given two files, a.clj...
(ns a)
(defn stuff-a [] :stuff-a)
(defn hello [] :hello)
And b.clj...
(ns b)
(defn stuff-b [] :stuff-b)
Say we have an ns 'x which is our running ns which uses 'a and 'b and
we wish to move the 'hello function into 'b in the files and then
dynamically reload so that 'x now has the correct mapping to 'hello
(i.e in 'b)
So in the Repl we'll create an empty ns 'x and change to it...
user=>(create-ns 'x)
#<Namespace x>
user=> (in-ns 'x)
#<Namespace x>
And load the two files with clojure.core/use ('x does not refer to
clojure.core so we can see all the ns mapping more clearly)...
x=> (clojure.core/use 'a)
nil
x=> (clojure.core/use 'b)
ni
So, 'x now sees...
(clojure.core/ns-refers 'x)
{hello #'a/hello, stuff-a #'a/stuff-a, stuff-b #'b/stuff-b}
And 'a and 'b contain...
x=> (clojure.core/ns-publics 'a)
{hello #'a/hello, stuff-a #'a/stuff-a}
x=> (clojure.core/ns-publics 'b)
{stuff-b #'b/stuff-b}
You've probably guessed it by now - to move 'hello from 'a to 'b we have to;
1.) Move it from file a.clj to b.clj
2.) Re-require and reload 'b
3.) Unmap it in the "refers" of 'x
4.) Re-refer 'b so 'x now sees it in 'b
5.) Unmap it in the "publics" of 'a
Here's the "dynamic reload" after 1.)...
2.) Reload 'b...
(clojure.core/require 'b :reload)
nil
x=> (clojure.core/ns-publics 'b)
{hello #'b/hello, stuff-b #'b/stuff-b}
x=> (clojure.core/ns-publics 'a)
{hello #'a/hello, stuff-a #'a/stuff-a}
Note that both 'a and 'b now have 'hello loaded but that's ok as 'x
still only knows about the version in 'a.
3, 4 and 5.)...
x=> (clojure.core/ns-unmap 'x 'hello)
nil
x=> (clojure.core/refer 'b)
nil
x=> (clojure.core/ns-unmap 'a 'hello)
nil
And so finally we have...
(clojure.core/ns-publics 'a)
{stuff-a #'a/stuff-a}
x=> (clojure.core/ns-publics 'b)
{hello #'b/hello, stuff-b #'b/stuff-b}
x=> (clojure.core/ns-publics 'x)
{}
x=> (clojure.core/ns-refers 'x)
{hello #'b/hello, stuff-a #'a/stuff-a, stuff-b #'b/stuff-b}
There still is a problem with this technique - 3.) and 4.) are not
atomic, so if 'hello is called between those two steps (which is
likely in a busy web server), that invocation will fail. Note that the
(ns-remove technique has the same problem, but at a coarser grain
level).
I think it's worth looking at encapsulating this whole reload
procedure into a properly atomically protected transaction as it will
be very useful in many server scenarios. This would probably require
changes to the namespace functions in core. Any thoughts?
Regards, Adrian.