tl;dr: I have a working patch. Review/try it from here:
https://github.com/laurentpetit/clojure/commit/0fe0c5238c00ef871ff6523d4b50d07a1bd86aa8
See below for a discussion on what's been done, and why it's been done this way.
OK, I've made some progress on this. Thanks to Christophe Grand also
for helping me decipher some dark corners of clojure.core loading
(more on this later).
Basically, the problem is that a namespace is considered as loaded if
it has been required, but not when (ns ....) has been called.
So a namespace enters the *loaded-libs* set only once it's been
(required) at least once.
A simple one-liner fix is to add, at the end of the macro-expanded
code for 'ns, the following line:
(dosync (commute *loaded-libs* conj '~name))
This has the advantage of addressing Stuart's issue with premature
calls to 'in-ns, since 'in-ns is not touched and would not add the ns
to *loaded-libs*
Except it will work in every case but when clojure.core itself is
loaded *if it has been AOT compiled*.
And that's an interesting story of itself :
The first line of clojure.core is (ns ^{:some "totally" :ignored
"metadata"} clojure.core)
- When clojure is not AOT compiled:
- the ns used is the "bootstrap" ns, defined in RT.java. It's doing
nothing more than the "bootstrap" in-ns: create the namespace, set it
as the current namespace
- note that in this case the metadata is ignored. The boostrap ns
discards it. That's why a short docstring is added later via an
alter-meta call
- When clojure is AOT compiled:
- clojure.core initializing is compiled in clojure/core__init.class,
and the 'ns code that will be called is not the bootstrap code, but
the compiled ns defined in clojure.core during the AOT compilation !
- this means that clojure.core is not loaded the same way depending
on whether clojure has been AOT compiled or not. In one case it's via
the bootstrap ns, in the other case via the result of the
macro-expansion of ns defined in itself (still following ? :-) )
- as a result, clojure/core__init.class evaluates code which can
only use interop calls to clojure.lang java clojure, because it's the
only thing available !!
This complicate things for me, when I want to add, at the end of 'ns
macro, code to access clojure.core/*loaded-libs*.
In either case, when applied to clojure.core itself, it's trying to
access a var yet to be defined.
Fortunately, there's an easy solution: a special case in the 'ns macro
for clojure.core.
Given all the specifics which apply to clojure.core already, I take it
for granted that is should not be a problem ?
Alternative solutions exist for solving, this:
- change the start of clojure/core.clj : replace the (ns ....
clojure.core) with (in-ns 'clojure.core)
- this would allow removing bootstrap 'ns code from RT.java (raw
in-ns would be sufficient)
- would remove the duplication of metadata for clojure.core (moved
in one place)
- solving the problem, since then the macro-expansion of ns would
never be called on clojure.core before it is initialized
- do things differently to solve the 'require problem:
- do not add, at the end of 'ns macro-expansion, code to add the ns
to *loaded-libs*, but rather just add a metadata to the ns as a sign
that 'ns has been called on it (and not just 'in-ns)
- and then, in 'load-lib, complement the test for loaded-libs with
the presence of this metadata on the ns
All in all, the above mentioned solutions seem to provide easy paths
towards solving the problem. There's just a choice to be made.
Thoughts, gentlemen?
2012/11/27 Jim Crossley <
jcros...@gmail.com>: