Macros iterating over undefined Symbols - macros

When applying a macro multiple times with a another macro, bare Symbols are not inserted into the current context:
(defmacro ty [type]
`(deftype ~type []))
(defmacro empties [& args]
(doseq [arg args]
`(ty ~arg))
)
(empties Base Person Animal)
;equivalent to:
;(ty Base)
;(ty Person)
;(ty Animal)
(derive ::Person ::Base)
(derive ::Animal ::Base)
(ty Me)
(prn ::Me)
(prn Me)
(empties Empty)
(prn ::Empty)
(prn Empty)
The last line gives: "Unable to resolve symbol: Empty in this context", even though when using the direct macro ty, it works. Any way to solve this? If possible without eval it would be much better.

(defmacro empties [& args]
(doseq [arg args]
`(ty ~arg)))
(empties Base Person Animal)
;equivalent to:
;(ty Base)
;(ty Person)
;(ty Animal)
This is wrong. Your empties call means that the macro expansion function for empties gets as arguments the symbols Base, Person, and Animal. It then evaluates the ty macro call for each, but does not return anything, as doseq always returns nil. So, the expanded code from that empties call is nil. You need to return a single form from your macro function. You should wrap the multiple forms into a do, and actually return all the subforms to that:
(defmacro empties [& args]
`(do ~#(map (fn [arg]
`(ty ~arg))
args)))

FWIW, I prefer to write #Svante's solution as
(defmacro empties [& args]
(cons `do
(for [arg args]
`(ty ~arg))))
which is also pretty close to the structure of your doseq approach.

Related

Clojure macro inside another macro: how to generate string from symbol

Probably the title is not 100% correct, but let me show you the issue:
(defmacro nvp!
[n width height]
`(q/defsketch (symbol (str (name '~n) "-viewport" ))
:title (name '~n))
In short: there's a macro called defsketch (it's part of the lib quil, but that does not matter). It's signature is basically defsketch [applet-name & options], and it creates something and binds it to a var called applet-name. For some reason, I want to wrap this into another macro that -- among other things -- takes an applet-name parameter, extends that name into applet-name-viewport and passes that to defsketch. I, however, am unable to figure out how to do that correctly (using macroexpand on the code above ((pprint (macroexpand(nvp test-name 500 500))), I get
(def(clojure.core/symbol (clojure.core/str (clojure.core/name 'my-namespace.core/test-name) "-viewport"))
(quil.applet/applet
:title (clojure.core/name 'my-namespace.core/test-name)))
(clojure.core/symbol (clojure.core/str (clojure.core/name 'my-namespace.core/test-name) "-viewport") -- this part looks good, but it should be evaluated somehow before passing it to the inner macro...
You need to unquote the form that generates the new symbol instead of the original symbol itself.
Here is a small example of how to accomplish this using a defn like macro for the inner macro:
(defmacro mydefn
[name & body]
`(defn ~name ~#body))
(defmacro defnview
[n & body]
`(mydefn ~(symbol (str (name n) "-viewport")) ~#body))
;; OR
(defmacro defnview
[n & body]
(let [n (symbol (str (name n) "-viewport"))]
`(mydefn ~n ~#body)))
Example:
(defnview foo [a] a)
;; => #'user/foo-viewport
(foo-viewport 1)
;; => 1
(macroexpand '(defnview foo [a] a))
;; => (def foo-viewport (clojure.core/fn ([a] a)))

How to write a macro resolve the symbol automatically

I'm always fancy that there is a method to resolve symbols automatically. For example, if I am in the "user" namespace and have not "used" symbols in the `clojure.string' namespace, instead of:
(clojure.string/split "a-b-c" #"-")
I want to write as this:
(call split "a-b-c" #"-")
I have simply implemented a `call' macro as this:
(defmacro call [sym & args]
`(let [fn# (first (distinct (remove nil? (map #(ns-resolve % '~sym) (all-ns)))))]
(if-not ((meta fn#) :macro)
(fn# ~#args)
(eval (list fn# ~#(map #(list 'quote %) args))))))
the following tests are always ok:
(call list 'a 'b)
(call apropos "list")
(call doc list)
(call doc clojure.string/split)
the problem happens when I pass a macro as the argument of `doc':
(call doc clojure.repl/doc)
then, there is a exception:
CompilerException java.lang.RuntimeException: Can't take value of a macro: #'clojure.repl/doc, compiling:(NO_SOURCE_PATH:112)
So, that's why? thanks
You can resolve the symbol at compile time in the macro like this:
(defmacro call [sym & args]
(let [f (first (distinct (remove nil? (map #(ns-resolve % sym) (all-ns)))))]
`(~f ~#args)))

Clojure defmacro loses metadata

I am trying to create a little Clojure macro that defs a String with a type hint:
(defmacro def-string [name value]
`(def ^String ~name ~value))
(def-string db-host-option "db-host")
When I macroexpand it, the type hint is lost:
(macroexpand '(def-string db-host-option "db-host"))
;=> (def db-host-option "db-host")
Never mind the wisdom of type hinting this.
Why is the macro losing the metadata? How do I write this macro, or any that includes metadata?
^ is a reader macro. defmacro never gets to see it. The hint is put on the list (unquote name). Compare for example (meta ^String 'x) with (meta ' ^String x) to see the effect.
You need to put the hint on the symbol.
(defmacro def-string
[name value]
`(def ~(vary-meta name assoc :tag `String) ~value))
And the usage:
user=> (def-string foo "bar")
#'user/foo
user=> (meta #'foo)
{:ns #<Namespace user>, :name foo, :file "NO_SOURCE_PATH", :line 5, :tag java.lang.String}
Metadata doesn't show up in a macroexpand since it's supposed to be "invisible".
If the macro is correct (which it isn't) you should be able to call (meta #'db-host-option) to inspect the meta data on the var.
Note that (def sym ...) inserts metadata on the var that it receives from the symbol. But ^Tag ~name sets the meta data on ~name (unquote name), not on the passed in symbol bound to name. It can't do anything else since ^Tag ... processing is done by the reader, which is already finished once macro expansion starts.
You want something like
(defmacro def-string [name value]
`(def ~(with-meta name {:tag String}) ~value))
user> (def-string bar 1)
#'user/bar
user> (meta #'bar)
{:ns #<Namespace user>, :name bar, :file "NO_SOURCE_FILE", :line 1, :tag java.lang.String}

How define variables in a macro, so that they available for the caller of the macro

I want to have a macro dbtest that can be used like this:
(dbtest
(prn test-object1)
(prn test-object2))
test-object1 and test-object2 should be variables which are defined by dbtest (because I need them everytime I use the dbtest macro). "prn" is just an example; I want to use arbitrary code inside the macro.
I tried this:
(defmacro dbtest [& body]
`(sql/with-connection db
(delete-all-tables)
(let [~'test-object1 (insert-object "foo")]
~#body)))
where insert-object is a function that insert something into the DB and returns a the corresponding data structure.
But it doesn't work: I get a "no such var" error
It works for me:
user=> (defmacro let-test [& body]
`(let [~'test-object1 123] ~#body))
#'user/let-test
user=> (let-test (+ test-object1 321))
444
Are you sure the problem isn't with the SQL-related calls?

Can I make a clojure macro that will allow me to get a list of all functions created by the macro?

I would like to have a macro which I'll call def-foo. Def-foo will create a function, and then will add this function to a set.
So I could call
(def-foo bar ...)
(def-foo baz ...)
And then there would be some set, e.g. all-foos, which I could call:
all-foos
=> #{bar, baz}
Essentially, I'm just trying to avoid repeating myself. I could of course define the functions in the normal way, (defn bar ...) and then write the set manually.
A better alternative, and simpler than the macro idea, would be to do:
(def foos #{(defn bar ...) (defn baz ...)} )
But I'm still curious as to whether there is a good way for the macro idea to work.
Do you want to end up with a set that has the names of functions in it (i.e. a set of Symbols), or a set containing vars (which resolve to functions)? If you want the former, you can add the symbols to an atom in the macro at compile-time, like Greg Harman's version.
If you want the latter, your macro must expand to code that does the atom-swapping after the function is defined. Remember that macros run at compile-time and the macro-expanded result runs at run-time; the function itself is not available until run-time.
(def all-foos (atom #{}))
(defmacro def-foo [x]
`(let [var# (defn ~x [] (println "I am" '~x))]
(swap! all-foos conj var#)))
If you want to be able to call the functions in this set, for example, you need to use the latter version.
user> (def-foo foo)
#{#'user/foo}
user> (def-foo bar)
#{#'user/foo #'user/bar}
user> ((first #all-foos))
I am foo
nil
Have the macro add the name of the new function to your set before creating the function, like so:
(def *foos* (atom (hash-set)))
(defmacro def-foo [name]
(swap! *foos* conj name)
`(defn ~name
[]
(println "This is a foo!")))
Result:
user=> (def-foo bar)
#'user/bar
user=> (def-foo baz)
#'user/baz
user=> (bar)
This is a foo!
nil
user=> (baz)
This is a foo!
nil
user=> *foos*
#<Atom#186b11c: #{baz bar}>