How to migrate definitions to another namespace ?

1 view
Skip to first unread message

Vagif Verdi

unread,
Aug 28, 2009, 11:55:32 PM8/28/09
to Clojure
I often refactor my code and move some functions to new modules.
Unfortunately i cannot load them, because clojure says that function
with such name is already loaded from another namespace. I could not
find nothing better but to close my clojure session (which means bring
down the web server) and start it back again.

Is there a way to unregister some names from a namespace without
reloading it ?

J. McConnell

unread,
Aug 29, 2009, 1:21:22 AM8/29/09
to clo...@googlegroups.com

Check out ns-unmap, I think that should do want you want. Contrib's ns-
utils might have some related functions too, but I'm not near a
computer to check.

HTH,

- J.

Timothy Pratley

unread,
Aug 29, 2009, 1:25:54 AM8/29/09
to Clojure
How about something like this?

user=> (ns fun)
fun=> (defn myfun [] 1)
fun=> (defn myfun2 [] 1)
fun=> (keys (ns-publics 'fun))
(myfun myfun2)

fun=> (doseq [s (keys (ns-publics 'fun))] (ns-unmap 'fun s))
fun=> (myfun)
java.lang.Exception: Unable to resolve symbol: myfun in this context
(NO_SOURCE_FILE:19)

Mike Hinchey

unread,
Aug 29, 2009, 1:33:30 AM8/29/09
to clo...@googlegroups.com
I use (remove-ns 'my-ns), then reload the entire file.

-Mike

Vagif Verdi

unread,
Aug 29, 2009, 2:22:18 AM8/29/09
to Clojure
Thx to all. ns-unmap and remove-ns are what i need.

From my CL experience i was looking for something like unitern.

Laurent PETIT

unread,
Aug 29, 2009, 2:24:53 AM8/29/09
to clo...@googlegroups.com
Could some kind of :force true flag be generally interesting for the def special form ? (:force true would force an unmap first, if necessary)

2009/8/29 Vagif Verdi <vagif...@gmail.com>

Adrian Cuthbertson

unread,
Aug 30, 2009, 2:46:52 AM8/30/09
to clo...@googlegroups.com
> 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.
Reply all
Reply to author
Forward
0 new messages