"immigrate" function for namespaces

56 views
Skip to first unread message

James Reeves

unread,
Jan 26, 2009, 5:20:48 PM1/26/09
to Clojure
Hi folks,

I've recently found myself having to choose between dividing
functionality between many specific namespaces, or having a few very
generic namespaces. In theory, being specific is the better choice, as
it allows users to more accurately pick what they want to use. But if
most of the time you import library X, you'd also like W, Y and Z, you
start to end up with a lot of lines of code just loading libraries.

Because I like to have my cake and eat it too, I wanted to keep my
specific namespaces, but also group them up in more generic ones. To
this end, I've created an "immigrate" function that interns every
public symbol in a namespace into the current namespace:

(defn- merge-meta!
"Destructively merge metadata from a source object into a target."
[source target]
(.setMeta target
(merge (meta source)
(select-keys (meta target) [:name :ns]))))

(defn immigrate
"Add all the public vars in a list of namespaces to the current
namespace."
[& namespaces]
(doseq [ns namespaces]
(require ns)
(doseq [[sym v] (ns-publics (find-ns ns))]
(merge-meta! v
(if (.isBound v)
(intern *ns* sym (var-get v))
(intern *ns* sym))))))

The idea is that you can bundle up a set of specific libraries into a
more general package, so your users don't have to type so much if they
just want the defaults.

- James

Cosmin Stejerean

unread,
Jan 26, 2009, 5:29:02 PM1/26/09
to clo...@googlegroups.com
Can you help me understand the difference between this and use (or :use in ns)?

--
Cosmin Stejerean
http://offbytwo.com

James Reeves

unread,
Jan 26, 2009, 5:48:54 PM1/26/09
to Clojure
On Jan 26, 10:29 pm, Cosmin Stejerean <cstejer...@gmail.com> wrote:
> Can you help me understand the difference between this and use (or :use in
> ns)?

use is internal to the current namespace. You can use other namespaces
without their publics being added to the current namespace. So:

=> (ns a)
=> (def x 10)
=> (ns b (:refer a))
=> x
10
=> (ns c (:refer b))
=> x
java.lang.Exception: Unable to resolve symbol: x in this context

You can see that just because b refers to a, it doesn't mean that c
gets x by referring to b. This is usually what you want, as you don't
want your namespaces to be cluttered up every time you call use or
refer. However, for libraries with lots of namespaces, sometimes you
want to group very specific namespaces into more generic packages, and
that's what immigrate does:

=> (ns a)
=> (def x 10)
=> (ns b)
=> (immigrate 'a)
=> x
10
=> (ns c (:refer b))
=> x
10

- James

Cosmin Stejerean

unread,
Jan 26, 2009, 5:58:29 PM1/26/09
to clo...@googlegroups.com
Thanks. I didn't realize that things brought in with refer weren't also exposed to other namespaces. That's actually something I do very often in Python. 
Reply all
Reply to author
Forward
0 new messages