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.

blog comments powered by Disqus