When I use lein run, why does it launch in the user namespace?

Hi all.

I am launching a test application with lein run using the following code:

(ns test.core
  (:gen-class))

(defn -main
  "Print the current namespace"
  [& args]
  (println *ns*))

I expected to get the test.core namespace, but instead it printed:

#object[clojure.lang.Namespace 0x5ffc5491 user]

I tried lein trampoline run as well but I got the same result.

By adding the line (in-ns 'test.core) I can get the results I expect, but I am still curious about the previous behavior.

Is this a side effect of using leiningen?

Should I always put (in-ns 'test.core) into my code just in case if I always want the namespace to be the namespace of my main function?

Agreed that this is somewhat surprising, but why do you need access to *ns* in your -main function?

A little trick that I use, when I need to refer to “the current namespace”, is to put this after the ns form:

(def ^:private my-ns *ns*)

That is resolved at load/compile time, before the code is actually run, so it binds my-ns to the actual namespace and not user which is the namespace that Clojure itself starts up in I believe.

3 Likes

I think I know why it does this actually. It makes sense when you think about it.

As namespaces are required, they get loaded and evaluated. They are evaluated top to bottom. So when they encounter ns, the namespace changes, and then when it encounters the -main, it is defining the function inside your test.core namespace.

After everything is required transitevely, it jumps back to the namespace of the first thing that was required, which in this case would be the bootstrapping namespace. In the case of lein, that is user.

Now from that namespace, it calls your -main function. That particular call stack is thus initiated from user.

When one namespace calls a function in another namespace, ns doesn’t change to the namespace of the called function, it stays to that of the calling namespace, which.is user in the case of lein bootstrapping your -main.

If you put your (println *ns*) outside of defn, just have it top level below your ns declaration for example, you will see it print test.core.

Finally, no, you should not have an in-ns call inside your -main function.

Is there any reason why it troubles you that your -main function executes within the context of user?

2 Likes

Why can’t you just constant the namespace name? You know it at coding time.

(ns com.bla)
(def my-ns 'com.bla)

Nevermind, I’m assuming you want the namespace object, not just its name.

In that case, you can also get it from the meta of any Var, as an alternative also:

(defn -main [& args]
  (println (:ns (meta #'-main))))

And this works as well:

(def my-ns (:ns (meta #'my-ns)))

Thanks for the reply.

It’s actually a contrived example for me to better understand Clojure modularity, Leiningen, etc.

I’m still in a newbie Clojure research and learning mode and I don’t know what I don’t know yet :).

Wanted to make sure it wasn’t some quick piece of information I simply did not know.

1 Like

Thanks for the reply.

I was making an assumption that *ns* is the namespace object at compile time not at runtime. It seems this assumption is incorrect.

Thanks for the reply.

The -main function context is good, I just didn’t understand how it works. I was confused because, looking at the code, it appears that the -main function is inside test.core namespace and not the user namespace.

There is a lot of information in your reply so I am going to spend some time absorbing what you have posted and, if I have any follow up questions, I will post them back on this thread. Thanks.

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.