Resolving names as they are compiled in Clojurescript
I think I found the bug in the Clojurescript compiler that I was
looking for yesterday. Just to refresh the memory, when defining a
function that has a function of the same name in cljs.core
, the
compiler will assume that you are trying to redefine the function
from cljs.core
, rather than the function in the namespace that you
are actually in. Since I am trying to redefine functions like /
and *
, this is a problem.
Let's look at the evidence. First, there's this:
(defmethod parse 'def [op env form name] (let [pfn (fn ([_ sym] {:sym sym}) ([_ sym init] {:sym sym :init init}) ([_ sym doc init] {:sym sym :doc doc :init init})) args (apply pfn form) sym (:sym args)] (assert (not (namespace sym)) "Can't def ns-qualified name") (let [name (munge (:name (resolve-var (dissoc env :locals) sym)))])))
The important line there is the last: when defining a new name,
resolve-var
is called on the symbol. Let's have a look at that
function. There is a cond
, and when none of the tests return
true, the following is done:
(munge (symbol (str (if (core-name? env sym) 'cljs.core (-> env :ns :name)) "." (munge (name sym)))))
This is seeing if the symbol name is in cljs.core
, and if it is
setting the namespace of the symbol to cljs.core
. Normally that
would be correct – one doesn't want to need to use
cljs.core
in
every file – but it doesn't allow for redefinition of functions
that are in cljs.core
(at least without completely shadowing
them).
What to do
So what can we do about this? The first thing that seems odd to me
is that the symbol being defined is being resolved first. Every
symbol that is defined is defined within its own namespace, so there
should be no need to resolve it. That suggests that we should be
able to take the part of resolve-var
that doesn't have a special
case for cljs.core
and put it into the parse
method. Something
like
(let [name (munge (:name (symbol (str (-> env :ns :name) "." (munge (name sym))))))])
might work in the parse
method.