I have a seq of namespace symbols:
(def namespace-symbols (namespace-1 namespace-2 namespace-3))
and I want to call the exact same method (say, run) over each one.
(map #{%/run} namespace-symbols)
won't work.
I also tried
(defmacro namespace-run
[namespace]
`(~(identity namespace)/run))
which also doesn't work. Is there any way to do this?
Update:
I finally came up with a macro like this:
(defmacro namespace-run
[namespace]
`(~(symbol (str namespace "/run"))))
(macroexpand-1 '(namespace-run foobar))
;=> (foobar/run)
Surely there is a better way to do this?
Check out clojure.core/ns-resolve
(doseq [ns namespace-symbols]
((ns-resolve ns 'run))) ;; resolve and execute fn
You can treat it just like a fn
(let [p (ns-resolve *ns* 'println)]
(p "Hello")
(p "World"))
Maybe use someting like symbol
clojure.core/symbol
([name] [ns name])
Returns a Symbol with the given namespace and name.
Related
Here's a working minimal example showing how Clojure can handle non-namespaced symbols:
(defmacro simple-macro [s]
(name `~s))
(str "And the answer is "
(simple-macro v1))
Now I'd like to do something more complicated. Inspired by this example:
(defn typical-closure []
(let [names (atom [])]
(fn [arg] (swap! names conj arg) #names)))
(def Q (typical-closure))
(Q 1)
(Q 2)
;; [1 2]
I now want to define a similar closure to take the names of undefined variables.
(defn take-names-fun []
(let [names (atom [])]
#((swap! names conj (simple-macro %)) (deref names))))
(def P (take-names-fun))
(P v1)
But this doesn't work as hoped; I get the error:
Unable to resolve symbol: v1 in this context
Is there a way to fix this so that we can add the name "v1" to the list of names defined above?
I tried using a macro instead (inspired by a syntax trick on page 21 of "Mastering Clojure Macros")... but this answer on ask.clojure.org says it doesn't make sense to define a closure over an atom in a macro.
(defmacro take-names-macro []
(let [names (atom [])]
`(fn [~'x] (swap! ~names conj (simple-macro ~'x)) (deref ~names))))
(def R (take-names-macro))
And indeed, I get another error here:
Can't embed object in code, maybe print-dup not defined:
However, there is no such restriction for using atoms inside defn. Maybe at the end of the day I need to put my symbols in a namespace...?
Not quite sure what it is that you're ultimately trying to accomplish.
But, since P is a function, it will always evaluate its arguments. So, if you pass it an undefined symbol, you'll get the error you got. Instead, you have to create a macro so that you can quote the undefined symbol (to stop the evaluation of the argument) and then pass that to P. Here is an example that does that.
user> (defn take-names-fun []
(let [names (atom [])]
(fn [arg] (swap! names conj (name arg)))))
#'user/take-names-fun
user> (def P (take-names-fun))
#'user/P
user> (defmacro PM [s] `(P (quote ~s)))
#'user/PM
user> (PM v1)
["v1"]
user> (PM v2)
["v1" "v2"]
user>
You might find the article on Evaluation in Clojure helpful.
#dorab's answer is nice.
But you could also tell yourself: "When entering undefined variables into a function, I have to quote them to avoid evaluation of them!"
So, after:
(defn typical-closure []
(let [names (atom [])]
(fn [arg] (swap! names conj arg) #names)))
(def Q (typical-closure))
Do:
user=> (Q 'v1)
[v1]
user=> (Q 'v2)
[v1 v2]
user=> (Q 3)
[v1 v2 3]
user=> (Q 'v4)
[v1 v2 3 v4]
user=>
In this way you don't need the macro and you can alternate between evaluated and not-evaluated arguments (undefined symbols).
So with the way fn's are written in clojure there is unfortunately no way to get the name of the var being passed as a param from within the fn body.. Someone with more experience with the clojure src may be able to explain better why that is, my initial guess would be that it has something to do with keeping thread local scopes isolated and lazy.
But there's absolutely nothing stopping you from writing a macro that wraps other macros using your closure idea!
Here's an example of how something like that may be written:
https://stackoverflow.com/a/11857444
How do you write a function to extract a docstring from a function?
I have been able to make the following work in a repl:
(:doc (meta (var identity)))
user=> "Returns its argument."
But when I wrap this in a function such as below:
(defn get-doc-string [f]
(:doc (meta (var f))))
The compiler says the following:
Unable to resolve var: f in this context
Is there another way to do this?
You have to quote, or better, syntax quote the symbol and use find-var:
(defn get-doc-string [f]
(:doc (meta (find-var f))))
Then you can do:
(get-doc-string `identity)
var is a special form which takes a symbol argument - you are passing f which cannot be resolved. You can create a macro:
(defmacro get-doc-string [sym]
`(:doc (meta (var ~sym))))
although clojure already implements this functionality in clojure.repl/doc.
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)))
how can i know if a function name provided as string is callable or not in the current context? something like:
(callable? "asdasd") ;; false
(callable? "filter") ;; true
thanks
You are looking for resolve,
(resolve (symbol "asd"))
returns nil
(resolve (symbol "filter"))
return #'clojure.core/filter
To check if a var is a function (credit goes to #amalloy):
(-> s symbol resolve deref ifn?)
Chances are if you need this, you're doing something wrong, but...
(defn callable?
[s]
(let [obj (try (eval (symbol s)) (catch Exception e))]
(and obj (fn? obj))))
(defn callable? [name]
(clojure.test/function? (symbol name)))
UPD. I found out that fn? checks only for interface Fn and doesn't work for resolved symbol. Though, clojure.test/function? does what is needed, so I updated an example.
I am trying to create a clojure macro that converts an entered symbol to a string. However, when I do this:
(defmacro convert-to-string [something]
`(call-converted "~something")
)
(macroexpand '(convert-to-string convert-this))
: I get :
(call-converted "~something")
: instead of :
(call-converted "~convert-this")
: does anyone show me how I could do this?
You might consider using keywords (or quoted symbols) and a function, rather than a macro:
(defn convert-to-string [x] (call-converted (name x)))
(convert-to-string :foo)
(convert-to-string 'foo)
If you really want a macro:
(defmacro convert-to-string [x] `(call-converted ~(name x)))
(macroexpand-1 '(convert-to-string foo))
=> (user/call-converted "foo")
I am in no way an expert on macros, but would this solve your problem:
(defmacro to-str [expr] (str expr))