Typically, each .clj file will have its own namespace. Clojure uses
the namespace to find where the corresponding .clj file is on the
classpath.
> Also, while we are at it, what are the naming conventions?
> The same as in Java packages?
> So I would use: "no.terjedahl.some.namespace.here" ?
Some people use this convention, but the majority of Clojure libraries
tend to use the following convention:
library-name.some.namespaces
For instance, here are some namespaces for the Ring library:
ring.middleware.params
ring.util.response
You can use namespaces of only one part, such as "lancet", but this
will result in class-files without a package. As I understand it, this
is generally discouraged, so in the cases where a library only needs
one namespace part, a ".core" is usually appended, e.g. "clout.core".
I don't think there are any official recommendations, but these are
the conventions used by many Clojure libraries, and build tools like
Leiningen.
- James
Unless you use dependency injection tricks. E.g.:
(ns foo)
(def somemap (atom {}))
(defn foo-inject [k v]
(swap! somemap assoc k v))
(defn foo-dosomething [a b c]
(let [d ((@somemap :injected-fn) a)]
...))
...
(ns bar)
(defn some-fn [a]
...
... depends indirectly on foo-dosomething,
... but avoids unbounded recursion if called
... by foo-dosomething
...)
(defn bar-init []
(foo/foo-inject :injected-fn some-fn)
...)
As long as bar-init is called before anything that calls
foo-dosomething/bar-init/etc., and the some-fn/foo-dosomething
recursion remains bounded, this is OK.
I had this come up recently in connection with a fairly complex system
that had to bootstrap itself. There was a low-level DAL that had to
fall back on certain in-memory defaults, which had to hold references
to functions that operated at a much higher level, eventually calling
a lookup function in some cases that depended in turn on the low-level
DAL. Cramming the DAL, the lookup engine, and some higher level
abstractions into a single namespace seemed excessive, and I wanted
these as three separate layers, but then the lowest of these layers
needed maps containing objects containing functions defined in the
highest.
To be exact, the lookup engine knows natively how to search data of
type X. But the system is extensible to be able to search data of
types Y, Z, etc. with the added search know-how being itself supplied
as data of type X. So the lookup engine uses the type of search to
first look up the appropriate lookup engine, if the type isn't X. This
leads to the lookup engine calling the DAL, which in turn needs to be
able to supply the "how to search for stuff" data of type X as soon as
the system has started up, which data needs to contain information on
searching for data of type Y as that's built in and not user-defined
and needed at a higher level of the system, and that information
contains search functions themselves, which punt to the lookup engine
...
The only alternative to the in-memory bootstrap procedure noted above
would be to rely on certain disk files being created ahead of the
first execution of the program on a new system. With the bootstrap
procedure, the system can easily generate the disk files if they're
absent, can usually recover if they get scrogged (and certain files
can be deleted to make it recover otherwise), and can eschew wasting
disk space storing "what it already knows", including only user-added
information in disk files.
For managing circular dependencies in general, the only alternative to
using a mutable of some sort to inject them is to defer their
resolution to runtime. Example 1:
(ns foo)
(defn foo-dosomething [a b c]
(let [d ((@(ns-resolve 'bar 'some-fn)) a)]
...))
...
(ns bar)
(defn some-fn [a]
...
... depends indirectly on foo-dosomething,
... but avoids unbounded recursion if called
... by foo-dosomething
...)
This variation just finds some-fn in namespace bar at runtime, so ns
foo will compile without ns bar, ns bar can then be compiled, and then
at runtime everything works. If things are slowed too much at runtime
in foo-dosomething, the resolution can be cached:
(ns foo)
(def some-fn (atom nil))
(defn foo-dosomething [a b c]
(let [f @some-fn
f (if f f (reset! some-fn @(ns-resolve 'bar 'some-fn)))
d (f a)]
...))
or you can define and use a memoized function that wraps @(ns-resolve x y).
If there are going to be many of these calls in your codebase you will
want to sugar things up:
(defn lookup [sym]
@(ns-resolve (symbol (namespace sym)) (symbol (name sym))))
(def lookup (memoize lookup))
(defmacro use [sym & args]
`((lookup (quote ~sym)) ~@args))
...
(ns foo)
(defn foo-dosomething [a b c]
(let [d (use bar/some-fn a)]
...))
This is nearly as nice as apply, though you have to fully qualify the
symbol bar/some-fn. The downside is, no compile time checking for
typos etc.; if there's no bar/some-fn at runtime you'll get an NPE
thrown out of lookup when foo-dosomething is called, as ns-lookup will
return nil and (deref nil), well ...
Basically, yes. It's possible to put different namespaces in the same
file, but then the `require` and `use` functions wouldn't work.
> Ah, but as in Java, the "big official libraries" such as java itself
> starts with i.e. java.swing. But if I was to publish a utilities
> library for swing, i would call it no.terjedahl.java.swing, so that it
> could be used by me or others as-is, without conflict with java.swing.
In theory this is a problem, but in practise it rarely is. Many
languages don't prefix their namespaces with domain names, such as
Ruby and Python, and they manage fine. Also, if your library doesn't
have a unique name (within the context of a language) it's confusing
for users and search engines, even if you can make the namespace
unique.
So in most cases, prefixing the domain on the namespace is
unnecessary, and it ties you to one domain. What happens if you move
domain, or someone else takes over the project? In my personal view,
the domain prefix looks rather unprofessional, though I guess that's a
matter of taste :)
- James
> > Ah, but as in Java, the "big official libraries" such as java itself
> > starts with i.e. java.swing. But if I was to publish a utilities
> > library for swing, i would call it no.terjedahl.java.swing, so that it
> > could be used by me or others as-is, without conflict with java.swing.
> In theory this is a problem, but in practise it rarely is. Many
> languages don't prefix their namespaces with domain names, such as
> Ruby and Python, and they manage fine.
Mostly fine, anyway. I've seen more than one Python private library
that unintentionally collided with standard library modules the author
didn't use and wasn't aware of. Trying to use both modules in the same
application was - um, interesting.
<mike
--
Mike Meyer <m...@mired.org> http://www.mired.org/consulting.html
Independent Software developer/SCM consultant, email for more information.
O< ascii ribbon campaign - stop html mail - www.asciiribbon.org
On Mon, 28 Mar 2011 20:50:05 -0400Mostly fine, anyway. I've seen more than one Python private library
James Reeves <jre...@weavejester.com> wrote:
> > Ah, but as in Java, the "big official libraries" such as java itself
> > starts with i.e. java.swing. But if I was to publish a utilities
> > library for swing, i would call it no.terjedahl.java.swing, so that it
> > could be used by me or others as-is, without conflict with java.swing.
> In theory this is a problem, but in practise it rarely is. Many
> languages don't prefix their namespaces with domain names, such as
> Ruby and Python, and they manage fine.
that unintentionally collided with standard library modules the author
didn't use and wasn't aware of. Trying to use both modules in the same
application was - um, interesting.
<mike
--
Mike Meyer <m...@mired.org> http://www.mired.org/consulting.html
Independent Software developer/SCM consultant, email for more information.
O< ascii ribbon campaign - stop html mail - www.asciiribbon.org
--
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