I am stuck on a seemingly basic thing. I have a namespace where I have some definitions:
(ns my-namespace)
(def my-definition "HELLO")
(def my-definition2 "HI")
Now, I want to use the value of the vars in my-namespace in a macro, but I want to retrieve the symbols dynamically. E.g.,
(defmacro my-macro [n]
(-> "my-namespace/my-definition" symbol resolve var-get))
Retrieving a symbol in such a manner works in a function (as long as the namespace is loaded), but not in a macro.
In a macro, the symbol cannot be resolved. I've tried quoting and unquoting but it still does not work.
Is it possible in a macro to use the value of a symbol created like that? If so, how?
Try this one:
(defmacro my-macro
[str]
(-> str symbol resolve deref))
The symbol cannot be resolved, because the namespace where it is defined is not loaded. You can load namespace by
(require 'my-namespace)
or in namespace declaration:
(ns macro-expansion-ns
(:require [my-namespace]))
Related
I have a macro defined like so in macros.cljh file:
(defmacro db-event [event-key params & body]
`(do
(re-frame.core/reg-event-db ~event-key
(fn [~'db [_# ~#params]]
(deep-merge ~'db ~#body)))
(defn ~(symbol event-key) ~params (re-frame.core/dispatch [~event-key ~#params]))))
which references a function deep-merge. This macro is defined in the namespace myapp.macros. Yet when I use the macro in a cljs file, I get the error:
Use of undeclared Var myapp.macros/deep-merge
even though deep-merge is also defined in the myapp.macros namespace
I have the macro required in macros.cljs file like so too:
(ns myapp.macros
(:require-macros [myapp.macros]))
I tried adding this to the cljs namespace declaration:
(:require [myapp.macros :refer [deep-merge]])
But that gives a circular dependency error. What am I doing wrong in refereeing the deep-merge function?
Macros in ClojureScript are more complicated, and must adhere to a 2-stage compilation process.
See these links for starters, then search out other blog postings & docs as well
https://clojurescript.org/about/differences#_macros
https://blog.fikesfarm.com/posts/2016-03-01-clojurescript-macro-sugar.html
In particular, the macro must be defined in a *.cljc or *.clj file, then used in the *.cljs file. You can see some examples in action in this template project:
https://github.com/cloojure/cljs-template
I just checked and it still works, although it uses the old version of Figwheel and has not yet been upgraded to Figwheel-Main (aka Figwheel 2.0).
You should be able to define the macro in the macros.clj file with deep-merge in macros.cljs. As you’ve seen, you can’t introduce circular dependencies. Your goal is to require the namespace, with the macros. :require-macros ensures that if you require the namespace, the macros are included. You should be able to use the macro by requiring the namespace normally:
(ns myapp.core
(:require [myapp.macros :as macros]))
(db-event …)
An alternative solution, instead of calling :require-macros in macros.cljs, is to :require the namespace with :include-macros true. As an example,
In macros.clj:
(ns myapp.macros)
(defmacro twice
[& body]
`(twice-helper (fn []
~#body)))
In macros.cljs:
(ns myapp.macros)
(defn twice-helper
[f]
(f)
(f))
Then we can use the macro in core.cljs:
(ns myapp.core
(:require [myapp.macros :as macros :include-macros true]))
(macros/twice (println "hello!"))
I have a macro as such:
Example:
(defmacro xxz [& fns] `(:body ~#(map (fn [[e1 e2]] `(~e2 "http://www.google.com")) fns)))
If I pass something like (xxz [client/get client/get]), the resulting macroexpand shows that the symbols weren't qualified:
(:body (client/get "http://www.google.com"))
This causes problems when something like this lands in a namespace that doesn't have client imported.
Does anybody know what to do?
This sounds like a design oversight with syntax-quote (in my opinion), though it's not clear what a general "fix" would look like.
here is a more minimal example:
yummly.mobile-api.main> (in-ns 'foo)
#namespace[foo]
foo> (clojure.core/refer-clojure)
foo> (require '[org.httpkit.client :as client])
nil
foo> (defmacro xxz [& fns]
`(~#fns))
#'foo/xxz
foo> (macroexpand-1 '(xxz client/get))
(client/get)
from a new namespace:
foo> (in-ns 'bar)
#namespace[bar]
bar> (macroexpand-1 '(foo/xxz client/get))
(client/get)
bar> (foo/xxz client/get)
CompilerException java.lang.RuntimeException: No such namespace: client, compiling:(*cider-repl api*:87:6)
the syntax-quote-form (aka `) looks at every symbol in the expression as it's being compiled and if it does not find a / then it assumes it's for the local namespace and appends the current namespace. It can only do this for symbols that are present in the actual macro at the time the macro is defined, not (as I would like it to be able to do) for symbols that are passed as arguments to the macro.
If you use fully namespace qualified symbols in your macro then you don't need to worry if namepace where people use your macro has these symbols mapped to anything, because they spell out the full path to the symbol in it's name. If you don't want to actually type the full namespace in the symbols you use in your macro, then you can use :refer [get] in the require statement in your ns expression, this will save you having to type out the name and cause them to be correctly namespace expanded at the time that the macro definition is evaluated.
Because syntax quote is producing unhygenic symbols, you will need to either both :require or :refer to the functions in your ns section at the top of the namespace for every client where these namespaces are passed to the function.
Another option is to find some way to make sure the symbol is available to the macro at the time the syntax-quote is compiled. This can be tricky in some cases.
I have namespace with debug utilities that are used only in development. I'd like to make them accessible in all namespaces without qualifier (same as symbols from clojure.core).
Let's say my project structure is as follows:
dbg_utils.clj:
(ns project.dbg-utils)
(defmacro dbg ...)
db.clj
(ns project.db)
(defn create-entity [...]
...)
After I'd like to fire up REPL and type something like this:
> (require 'project.db)
> (require 'project.dbg-utils)
> (globalize-symbol 'project.dbg-utils/dbg)
And after use dbg macro without qualifier:
(ns project.db) ;; no require of project.dbg-utils
(defn create-entity [...]
(dbg ...) ;; and no qualifier here
...)
Is anything like globalize-symbol (or close to this) available?
Leiningen provides the :injections feature and the :user profile for that.
This article shares some pointers on how to do that. It basically works by adding the debugging functions you want to clojure.core and since all public vars from this namespace are always included when using the ns macro (unless you specify otherwise), you will have them available in all your namespaces.
I have a dictionary of words stored in a vector in one ns (ns dictionary.core) and I want to have access the vector in another namespace, for example, (ns clojure-project.core) How should I do it? I have been researching the concept of namespaces for some time and I am still confused as to how I can "import" variables defined in another file into my current project.
(ns clojure-project.core
(:require [dictionary.core :as dict]))
(defn choose-a-word []
(rand-nth (dict/words)))
Namespace declarations have many options, so it can be very confusing! Limiting yourself to the above form is in my opinion good style.
I've this macro declared in a namespace named "helpers"
(defmacro reply [name-key & arguments] ;;macro use BUS, it needs to be declared in this namespace
~(<! (reply* (~name-key BUS) arguments)))
I need use it in other namespace "core" after initialized BUS in this namespace with a map
(def BUS {:something "a"})
(reply ...)
the namespace helpers only compile if BUS is declared in this namespace...I can declare it and then initialize it in my specific namespace
***helpers
(def BUS)
(declare BUS) ;;alternative
(defmacro reply... ) ;;using BUS in its body!
***other namespace
(def BUS {:a "b"})
(reply ...) ;; this macro use BUS
but this fails
BUS already refers to: #'yourvertxproject.helper-fun/BUS in namespace: test1.core, compiling:(test1/core.clj:13:1)
which is the correct way for do this?...
note: I've notice than some libs achieve this, for instance in korma db, you initialize the variable with the db path and configuration and then you can use the different functions which depends of this variable....
thanks!...
I believe you are looking for dynamic variables.
In your "helpers" namespace, declare *bus* (not "BUS") using
(def ^:dynamic *bus*)
before creating the macro.
Then, in the namespace where you are using this, do
(binding [*bus* {:a "b"}]
...)
(Be aware that binding is thread-local.)
In this case I would also refer the "helpers" using an :as shortcut with a qualified namespace to make it easier to see that *bus* is defined elsewhere.