I am trying to do the following:
(ns ns-test.core
(:use [ns-test.a :as a]
[ns-test.b :as b]))
(def test-map {:key "a"})
(defmulti print-ns :key)
(defmethod print-ns "a" [input-map]
(a/foo input-map))
(defmethod print-ns "b" [input-map]
(b/foo input-map))
with namespaces a and b that look like this:
(ns ns-test.a)
(defn foo [x]
(println x "I'm in namespace A."))
and
(ns ns-test.b)
(defn foo [x]
(println x "I'm in namespace B."))
but when I try to load these classes into the REPL, I get this:
user=> (use 'ns-test.core :reload)
CompilerException java.lang.IllegalStateException: foo already refers to: #'ns-test.a/foo in namespace: ns-test.core, compiling:(ns_test/core.clj:1:1)
Why does this conflict between a/foo and b/foo exist, and how can I prevent it? (Isn't the whole point of namespaces and namespace qualification to allow me to have two different functions of the same name?)
You probably wanted to :require the namespaces a and b instead of :use. :use interns the namespace symbols to the current namespace, thus the conflict.
Related
I would like a macro this-ns such that it returns the namespace of the location where it is being called. For instance, if I have this code
(ns nstest.main
(:require [nstest.core :as nstest]))
(defn ns-str [x]
(-> x (.getName) name))
(defn -main [& args]
(println "The ns according to *ns*:" (ns-str *ns*))
(println "The actual ns:" (ns-str (nstest/this-ns))))
I would expect that calling lein run would produce this output:
The ns according to *ns*: user
The actual ns: nstest.main
What I came up with as implementation was the following code:
(ns nstest.core)
(defmacro this-ns []
(let [s (gensym)]
`(do (def ~s)
(-> (var ~s)
(.ns)))))
It does seem to work, but it feels very hacky. Notably, in the above example it will expand to def being invoked inside the -main function which does not feel very clean.
My question: Is there a better way to implement this-ns to obtain the namespace where this-ns is called?
here is one more variant:
(defmacro this-ns []
`(->> (fn []) str (re-find #"^.*?(?=\$|$)") symbol find-ns))
the thing is the anonymous function is compiled to a class named something like
playground.core$_main$fn__181#27a0a5a2, so it starts with the name of the actual namespace the function gets compiled in.
Can't say it looks any less hacky, then your variant, still it avoids the side effect, introduced by def in your case.
Interesting question. I would never have guessed that your code would output user for the first println statement.
The problem is that only the Clojure compiler knows the name of an NS, and that is only when a source file is being compiled. This information is lost before any functions in the NS are called at runtime. That is why we get user from the code: apparently lein calls demo.core/-main from the user ns.
The only way to save the NS information so it is accessible at runtime (vs compile time) is to force an addition to the NS under a known name, as you did with your def in the macro. This is similar to Sean's trick (from Carcingenicate's link):
(def ^:private my-ns *ns*) ; need to paste this into *each* ns
The only other approach I could think of was to somehow get the Java call stack, so we could find out who called our "get-ns" function. Of course, Java provides a simple way to examine the call stack:
(ns demo.core
(:use tupelo.core)
(:require
[clojure.string :as str]))
(defn caller-ns-func []
(let [ex (RuntimeException. "dummy")
st (.getStackTrace ex)
class-names (mapv #(.getClassName %) st)
class-name-this (first class-names)
class-name-caller (first
(drop-while #(= class-name-this %)
class-names))
; class-name-caller is like "tst.demo.core$funky"
[ns-name fn-name] (str/split class-name-caller #"\$")]
(vals->map ns-name fn-name)))
and usage:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test)
(:require
[clojure.string :as str]
[demo.core :as core]))
(defn funky [& args]
(spyx (core/caller-ns-func)))
(dotest
(funky))
with result:
(core/caller-ns-func) => {:ns-name "tst.demo.core", :fn-name "funky"}
And we didn't even need a macro!
I am trying to define a multimethod and its implementation in a separate file. It goes something like this:
In file 1
(ns thing.a.b)
(defn dispatch-fn [x] x)
(defmulti foo dispatch-fn)
In file 2
(ns thing.a.b.c
(:require [thing.a.b :refer [foo]])
(defmethod foo "hello" [s] s)
(defmethod foo "goodbye" [s] "TATA")
And in the main file when I am calling the method I define something like this:
(ns thing.a.e
(:require thing.a.b :as test))
.
.
.
(test/foo "hello")
When I do this I get an exception saying "No method in multimethod 'foo'for dispatch value: hello
What am I doing wrong? Or is it not possible to define implementations of multimethods in separate files?
It is possible. The problem is because thing.a.b.c namespace isn't loaded. You have to load it before using.
This is a correct example:
(ns thing.a.e
(:require
[thing.a.b.c] ; Here all your defmethods loaded
[thing.a.b :as test]))
(test/foo "hello")
I load a function say-hi from namespace learning.greeting
(use 'learning.greeting)
When I try to re-defn the say-hi function under the current (user) namespace, I got the error:
CompilerException java.lang.IllegalStateException: say-hi already refers to: #'learning.greeting/say-hi in namespace: user, compiling:(NO_SOURCE_PATH:1:1)
So how to unload the function from other namespaces?
If you want to get rid of a direct mapping to a Var from another namespace at the REPL, say
(ns-unmap 'current-namespace 'local-alias)
Example:
user=> (ns-unmap *ns* 'reduce)
nil
user=> (reduce + 0 [1 2 3])
CompilerException java.lang.RuntimeException: Unable to resolve symbol: reduce in this context, compiling:(NO_SOURCE_PATH:2:1)
Local alias will differ from the actual name of the Var if :rename was used:
(use '[clojure.walk
:only [keywordize-keys]
:rename {keywordize-keys keywordize}])
To remove all mappings pointing at Vars in clojure.walk:
(doseq [[sym v] (ns-map *ns*)]
(if (and (var? v)
(= (.. v -ns -name) 'clojure.walk))
(ns-unmap *ns* sym)))
Do you really want to remove say-hi from learning.greeting? If not, it might be better to use require in this situation. Instead of (use 'learning.greeting), execute:
(require `[learning.greeting :as lg])
Then you can refer to the original definition as lg/say-hi, and you can define a new version in the current namespace, e.g. as
(def say-hi [x] (lg/say-hi (list x x))
(I don't know whether that makes sense for the say-hi function, but the general point is the same regardless.)
both use and require have an :exclude parameter for just this situation:
(use '[learning.greeting :exclude [say-hi]])
or more preferably use require:
(require '[learning.greeting :refer :all :exclude [say-hi]])
or when you are working in a normal namespace putting all this in the ns form is preferred:
(ns my-namespace
(:require [learning.greeting :refer [ function1 function2] :as greeting]
I'm trying to accurately determine the namespace of a function's caller. Looks like *ns* is determined by the namespace at top of the call stack.
user=> (ns util)
nil
util=> (defn where-am-i? [] (str *ns*))
#'util/where-am-i?
util=> (ns foo (:require [util]))
nil
foo=> (util/where-am-i?)
"foo"
foo=> (ns bar)
nil
bar=> (defn ask [] (util/where-am-i?))
#'bar/ask
bar=> (ask)
"bar"
bar=> (ns foo)
nil
foo=> (util/where-am-i?)
"foo"
foo=> (bar/ask)
"foo"
foo=>
Is there some other meta data I can rely on or do I need to specify this manually?
This is not possible. In the repl, *ns* is always set to the namespace in which the repl is; at runtime it is usually clojure.core, unless someone goes to the trouble to set it, which is uncommon.
I'm not sure what your complete use case is, but judging from your example you want #'bar/ask to return its own namespace instead of returning a function that resolves the current namespace. You could simply use def instead of defn. Here is an example following on from what you did:
util=> (in-ns 'bar)
#<Namespace bar>
bar=> (def tell (util/where-am-i?))
#'bar/tell
bar=> (in-ns 'foo)
#<Namespace foo>
foo=> (refer 'bar :only '[tell])
nil
foo=> tell
"bar"
Hope this helps!
I know how to forward declare a var for the current namespace. Instead, I want to declare a var from another namespace. How do I do this? This will help me eliminate a circular load dependency.
At the moment, this is what I've tried:
; this_ns.clj
(ns my-project.this-ns
(:require ...))
(ns my-project.other-ns)
(declare other-func)
(ns my-project.this-ns) ; return to original namespace
(defn func-1
[]
(my-project.other-ns/other-func))
It works, but I don't like it.
I think the solution you already have is the easiest one. If you wrap it into a macro it doesn't even look that bad anymore:
(defmacro declare-extern
[& syms]
(let [n (ns-name *ns*)]
`(do
~#(for [s syms]
`(do
(ns ~(symbol (namespace s)))
(declare ~(symbol (name s)))))
(in-ns '~n))))
Call it with:
(declare-extern my.extern.ns/abc) ;; => #<Namespace ...>
my.extern.ns/abc ;; => #<Unbound Unbound: #'my.extern.ns/abc>