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>
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 want to extend and redefine a clojure.core macro. For example, how can I redefine clojure.core/defn while using the original definition?
This is a bit tricky, but by aliasing the macro in clojure.core this is possible.
Open the repl and follow the steps below.
➜ ~ clj
Clojure 1.9.0
First alias clojure.core/defn to something else:
user=> (in-ns 'clojure.core)
#object[clojure.lang.Namespace 0x48e92c5c "clojure.core"]
clojure.core=> (defmacro defn-holder [])
#'clojure.core/defn-holder
clojure.core=> (alter-var-root #'defn-holder (constantly (var-get #'defn)))
#object[clojure.core$defn__5154 0xd3957fe "clojure.core$defn__5154#d3957fe"]
Next, create a new defn macro that uses the alias definition
clojure.core=> (in-ns 'user)
#object[clojure.lang.Namespace 0x64ba3208 "user"]
user=> (defmacro defn [& args] `(do (println "aliased version")(clojure.core/defn-holder ~#args)))
WARNING: defn already refers to: #'clojure.core/defn in namespace: user, being replaced by: #'user/defn
#'user/defn
user=> (defn foo [a])
aliased version
#'user/foo
user=> (foo 1)
nil
However it doesn't work for all namespaces yet:
user=> (ns bar)
nil
bar=> (defn foo [a])
#'bar/foo
We need to redefine defn in clojure.core with our new definition:
bar=> (in-ns 'clojure.core)
#object[clojure.lang.Namespace 0x48e92c5c "clojure.core"]
clojure.core=> (alter-var-root #'defn (constantly (var-get #'user/defn)))
#object[user$defn 0x37052337 "user$defn#37052337"]
Now it works:
clojure.core=> (in-ns 'bar)
#object[clojure.lang.Namespace 0x37efd131 "bar"]
bar=> (defn foo [a])
aliased version
#'bar/foo
Let's say that I have the following :
(defn my-fn [params]
(make-things (a-fn [ctx]
(do-this params)))
Now I wish to split this into different files, such that a-fn is in another namespace :
(defn my-fn [params]
(make-things my.ns/a-fn))
But the problem now is that the params doesn't close over my function anymore. How should do that ?
Rewrite:
(defn my-fn [params]
(make-things (fn a-fn [ctx]
(do-this params)))
into
(defn my-fn [params]
(make-things ((fn a-fn-o [p]
(fn a-fn [ctx]
(do-this p)))
params)))
This is what the compiler does for you when you close over variables.
Then it should be clear what to do in your other file. The function returns a function and you pass in params:
(defn my-fn [params]
(make-things (my.ns/a-fn params)))
;; my.ns
(defn a-fn [params]
(fn [ctx] (do-this params)))
Btw, your ctx parameter is unused.
Just so that's mentioned too, you might instead of closures also want to consider using a Var to scope the params dynamically rather than lexically. This is especially useful if they're "implicit arguments" to several related functions in my.ns.
(ns my.ns)
(def ^:dynamic *params* ...) ;;Optional default value here
(defn a-fn [...]
(do-this *params* ...))
and then in the calling ns
(defn my-fn [params]
(binding [my.ns/*params* params]
(my.ns/a-fn ...)))
This is how for instance with-out-str alters the behaviour of any print variant both in its body and in subcomputations - by rebinding *out* to an empty string writer.
You could also use partial:
(ns my.ns)
(defn a-fn [params ctx]
(do-this params))
Then in the calling context:
(defn my-fn [params]
(make-things (partial my.ns/a-fn params)))
EDIT: Turned out I was using require instead of :require in the namespace declaration. With :require, tools.namespace refreshes the logging namespace, and the problem goes away. I still find it curious, however, that the expression (eval `(var ~(symbol "A/func"))) does not work in the situation described below (that is, if B below is not refreshed).
Summary: I'm using tools.namespace. If I have namespaces A and B, and in B do (eval `(var ~(symbol "A/func"))), and (tools.namespace/refresh) and run the code, that works. But if I make a change to A, do (tools.namespace/refresh), so that only A refreshes, then running that expression gives the error: Cannot resolve var: A/func in this context, even though A/func exists. Why?
Longer version:
In my project, I have a logging module/namespace that uses robert-hooke (see below). I'm using tools.namespace to reload my code when I make changes.
The problem is the following: When I want to log (my logging currently just prints) something, I list the functions that I want to log in my logging namespace and do (t.n/refresh). That works. But if I make changes to the the namespaces that contain the functions that I want to log, and do (t.n/refresh) without making changes to the logging namespace, the logging no longer works (for the functions that have been refreshed). As soon as I make a change to logging, so that it too is refreshed by tools.namespace, it starts working again.
So, it's like the vars in namespaces that have been refreshed don't properly get their logging hooks. But I don't understand why.
Below is my logging namespace. I call add-logging-wrappers each time I run my program.
If I add (eval `(var ~(symbol "sv/register-damage"))) inside add-logging-wrappers, that's fine when logging has just been refreshed and the logging works. But those times the logging does not work, that expression causes the error Cannot resolve var: sv/register-damage in this context.
(ns game.logging
(require [robert.hooke :as rh]
[clojure.pprint :as pp]
[game.server.core :as sv]
[game.client.core :as cl]
[game.math :as math]
(game.common [core-functions :as ccfns]
[graphics :as gfx])
(game.server [pathfinding :as pf]))
(:use [game.utils]))
(defn log-println [name type object]
(println (str (current-thread-name) " // " name " " type ":\n"
(with-out-str
(pp/pprint object)))))
(defn print-output [name f & args]
(let [result (apply f args)]
(log-println name "output" result)
result))
(defn print-input [name f & args]
(log-println name "input" args)
(apply f args))
(defn print-call [name f & args]
(println (str (current-thread-name) "//" name))
(apply f args))
(defmacro make-name-var-list [fn-list]
`[~#(for [fn fn-list]
[(str fn) `(var ~fn)])])
(defmacro defloglist [name & fns]
`(def ~name (make-name-var-list [~#fns])))
(defn add-hooks [name-vars & wrappers]
(when (seq wrappers)
(doseq [[name var] name-vars]
(rh/add-hook var (partial (first wrappers) name)))
(recur name-vars (next wrappers))))
(defn get-ns-name-vars [ns-sym]
(-> (the-ns ns-sym) (#(.name %)) ns-interns))
(defn add-hooks-to-ns [ns-sym & wrappers]
(apply add-hooks (get-ns-name-vars ns-sym) wrappers))
(defloglist log-both
sv/distribute-exp ;; <--- things to log
sv/register-damage
sv/give-exp)
(defloglist log-input)
(defloglist log-output)
(defn add-logging-wrappers []
(dorun (->> (all-ns) (map #(.name %)) (mapcat ns-interns) (map second)
(map rh/clear-hooks)))
(add-hooks log-both print-output print-input)
(add-hooks log-input print-input)
(add-hooks log-output print-output))
I often want to run a small snippet of code in another namespace - perhaps a copy/pasted snippet of DSL code for example, and I'd like to avoid having to either:
Add a bunch of use clauses to my current namespace declaration. This makes the ns declaration messy, adds extra maintenance work and sometimes risks name clashes.
Add require clauses and be forced to add a namespace qualifier or alias to everything. Now my DSL code is much messier.
Ideally I'd prefer to be able to do something like:
(with-ns my.namespace
(foo bar baz))
Where foo, bar might be symbols within my.namespace, but baz is a symbol in the current (enclosing) namespace. So the code is running in something like a "local" namespace that "uses" my-namespace within its scope but otherwise doesn't affect the surrounding namespace.
Is there a standard/better way to do this? Or is this a crazy thing to want to do?
Try this one:
(defmacro with-ns [[namespace symbols] & body]
`(do (use '[~namespace :only ~symbols])
(let [result# (do ~#body)]
(doseq [sym# (map #(:name (meta (val %)))
(filter #(= (name '~namespace)
(str (:ns (meta (val %)))))
(ns-refers *ns*)))]
(ns-unmap *ns* sym#))
result#)))
(with-ns [clojure.string [split upper-case]]
(split (upper-case "it works!") #" "))
-> ["IT" "WORKS!"]
After work it removes used symbols from current ns.
This can be achieved using a macro as shown below.
NOTE: It may break in certain cases as I just tried it with a simple example
;Some other ns
(ns hello)
(def h1 10) ;in hello
(def h2 11) ;in hello
;main ns in which executing code
(ns user)
(defmacro with-ns [target-ns body]
(clojure.walk/postwalk
(fn [val]
(if (symbol? val)
(if (resolve (symbol (str target-ns "/" val)))
(symbol (str target-ns "/" val))
val) val)) body))
(def u1 100) ;in user
(with-ns hello (do (+ h1 u1))) ;110
I eventually found a macro in the old Clojure contrib that does part of this quite neatly:
(defmacro with-ns
"Evaluates body in another namespace. ns is either a namespace
object or a symbol. This makes it possible to define functions in
namespaces other than the current one."
[ns & body]
`(binding [*ns* (the-ns ~ns)]
~#(map (fn [form] `(eval '~form)) body)))