Passing state between macros - macros

What is the best way to create a set of macros that share information with each other at compile time in Clojure?
I'm looking for some way of having macros know what previous macros have done and act accordingly, e.g. how would you implement macros that can be used like the following:
(macro-block-with-data ["A" "B" "C"]
(process-next-data-item) ; macro expands using "A"
(process-next-data-item) ; macro expands using "B"
(process-next-data-item)) ; macro expands using "C"
Clarifications
This needs to happen at compile time with macros, i.e. not with regular functions at runtime

Is it lexically scoped? If so, you can do something like the following, which uses stateful iterators (kind of ugly, though).
(defmacro process-next-data-item []
`(println "step" (.next ~'__state)))
(defmacro macro-block-with-data [dat & body]
`(do
(let [~'__state (.iterator ~dat)]
~#body)))
As an example:
(defn test []
(macro-block-with-data ["A" "B" "C"]
(println "start")
(process-next-data-item) ; macro expands using "A"
(println "middle")
(process-next-data-item) ; macro expands using "B"
(println "almost done")
(macro-block-with-data [ "NestedA" "NestedB" ]
(println "starting nested")
(process-next-data-item)
(process-next-data-item)
(println "done nested"))
(process-next-data-item)
(println "done")))
... results in ...
user> (test)
start
step A
middle
step B
almost done
starting nested
step NestedA
step NestedB
done nested
step C
done
nil

Related

How to write a Clojure macro that mimics Ruby's %w operator

I'm trying to write my first ever macro in Clojure. I want to mimic Ruby's %w{} operator, which works like this:
irb(main):001:0> %w{one two three}
=> ["one", "two", "three"]
I want to write a function similar in Clojure that returns a vector of words. Here is how it would look:
user=> (%w one two three)
=> ["one" "two" "three"]
I know this is something that cannot be defined as an ordinary function because the symbols would be evaluated before applying and we would see something like this:
user=> (%w one two three)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: one in this context, compiling:(NO_SOURCE_PATH:1:1)
Here is my attempt at a macro:
(defmacro %w [& words]
(map str (vec words)))
But it doesn't work.
user=> (%w one two three)
ClassCastException java.lang.String cannot be cast to clojure.lang.IFn user/eval801 (NO_SOURCE_FILE:1)
Why is this happening?
ANSWERS
So the problem was that the macro actually returned the correct output, but then the repl tried to evaluate it and "one" is not a valid function.
Thanks to the answers below, here are two correct macros that solve this problem:
(defmacro %w-vec [& words]
"returns a vector of word strings"
(mapv str (vec words)))
(defmacro %w-list [& words]
"returns a list of word strings"
(cons 'list (map str words)))
It does not work because after macro-expansion clojure tries to evaluate ("one" "two" "three"), inducing your error message
user=> (%w one two three)
ClassCastException java.lang.String ("one") cannot be cast to clojure.lang.IFn (interface for callable stuff) user/eval801 (NO_SOURCE_FILE:1)
now you could do that
(defmacro %w [& words]
(mapv str (vec words)))
generating a vector
or that
(defmacro %w [& words]
(cons 'list (mapv str (vec words))))
generating (list "one" "two" "three")
or with syntax quote
(defmacro %w [& words]
`(list ~#(map str (vec words))))
Macros can be thought of as regular functions that take unevaluated code (as data structures) and return new code (as data), which then gets evaluated.
Your macro is returning ("one" "two" "three"), which will evaluate as a call of the function "one" with arguments "two" "three".
Straightforward solutions would be to make your macro return either (list "one" "two" "three") or a vector ["one" "two" "three"].

Clojure macro to process multiple function metadata

In Clojure, how do I make a library macro which processes supplied functions metadata and return some result? Amount of functions is unlimited and they should be passed without being boxed into a sequence ((my-macro fn1 fn2) instead of (my-macro [fn1 fn2]))
Say, we expect function vars having :foo keys in meta and the macro concatenates their values. The following snippet should work in REPL (considering my-macro is in the namespace):
user=> (defn my-func-1 {:foo "bar"} [])
(defn my-func-1 {:foo "bar"} [])
#'user/my-func-1
user=> (defn my-func-2 {:foo "baz"} [])
(defn my-func-2 {:foo "baz"} [])
#'user/my-func-2
user=> (my-macro my-func-1 my-func2)
(my-macro my-func-1 my-func2)
"barbaz"
I tried several approaches but was only able to process single function so far.
Thanks!
Try this:
(defmacro my-macro [& fns]
`(clojure.string/join (list ~#(map (fn [x] `(:foo (meta (var ~x)))) fns))))
(defn ^{:foo "bar"} my-func-1 [])
(defn ^{:foo "baz"} my-func-2 [])
(my-macro my-func-1 my-func-2) ;; => "barbaz"
How it Works
If you expand the macro you can start to see the parts in play.
(macroexpand '(my-macro my-func-1 my-func-2))
(clojure.string/join
(clojure.core/list (:foo (clojure.core/meta (var my-func-1)))
(:foo (clojure.core/meta (var my-func-2)))))
(var my-func-1)
Function metadata is stored on the var, so using (meta my-func-1) is not sufficient. But, var is a special form and does not compose like a normal function.
(fn [x] `(:foo (meta (var ~x))))
This anonymous function exists inside an escaped form, so it is processed inside the macro to produce the output forms. Internally it will create a the (:foo (meta (var my-func-1))) form by first backtick escaping the outer form to declare it a literal, and not evaluated, list and then unescaping the x var with a tilde to output the value instead of the symbol.
`(clojure.string/join (list ~#(map (fn [x] `(:foo (meta (var ~x))))
fns)))
This entire form is backtick escaped, so it will be returned literally. But, I still need to evaluate the map function generating the (:foo (meta (var my-func-1))) form. In this case I have unescaped, and spliced (#) the result of, the map form directly. This first evaluates the map function and returns a list of generated forms, and then takes the contents of that list and splices it into the parent list.
(defmacro test1 [x] `(~x))
(defmacro test2 [x] `(~#x))
(macroexpand '(test1 (1 2 3))) ;; => ((1 2 3))
(macroexpand '(test2 (1 2 3))) ;; => (1 2 3)
You could also split out the map function in a let statement before hand for slightly more readability.
(defmacro my-macro [& fns]
(let [metacalls (map (fn [x] `(:foo (meta (var ~x)))) fns)]
`(clojure.string/join (list ~#metacalls))))

Reverting to global scope in macro in clojure?

I want to make a macro which defines a function outside of the local variable scope, as to catch errors where the programmer (aka me) is incorrectly referencing variables in the local scope. It should only allow variables declared in the first argument of the macro.
For example, the following should compile fine:
(let [ foo 'x
bar 'y ]
(my-macro [ foo ] ;; this designates that foo can be used
(print foo)
)
)
but, the following should fail to compile, because "bar" wasn't declared in the first argument of my-macro:
(let [ foo 'x
bar 'y ]
(my-macro [ foo ] ;; only foo is declared here, but bar
;; is used, so should fail to compile
(print foo)
(print bar) ;; <-- should fail here
)
)
Besides the error checking, the macro needs to return a vector of the values of the variables used and a function containing the body. This is what I have so far.
(defmacro my-macro
[ declared-vars-in-use & body ]
`[~declared-vars-in-use (fn [~#declared-vars-in-use]
~#body
)
]
)
The only thing I don't know how to do is enforce the compilation error when referencing a symbol from the local scope that isn't declared in my-macro ("bar" in the above example). Forgive me if I'm using incorrect terms, and hopefully you can understand this, I am still a newbie at clojure.
(Updated with a version of explicit-closure free from the considerable limitations of the original approach.)
The macro below returns a function which closes over the specified locals and accepts specified additional arguments. If you only want a function which doesn't close over any locals at all (in other words, whose body only uses no external locals), which is what my-macro would produce if given the desired ability to prevent access to locals not on the declared-vars-in-use list (as the locals on the list would be shadowed by the parameters), you can simply eval an appropriate fn form, as eval ignores locals. (So, skip close-over and the let around the inner list* in the snippet below.)
(defmacro explicit-closure [close-over params & body]
(eval (list 'let
(vec (interleave close-over (repeat nil)))
(list* 'fn params body)))
`[~close-over (fn [~#params] ~#body)])
Note that the call to eval here happens during compilation and is only meant to coax the compiler into complaining if the function body references the wrong locals. If the body does not use disallowed locals or otherwise cause the eval call to fail, regular code for the function is emitted with no further checks or eval-induced compilation at runtime.
From the REPL:
(let [foo 1
bar 2]
(explicit-closure [foo] [x] (+ foo x)))
;= [[1] #<user$eval1887$fn__1888 user$eval1887$fn__1888#39c4d0cd>]
(let [foo 1
bar 2]
(let [[vals f] (explicit-closure [foo] [x] (+ foo x))]
(prn vals)
(f 3)))
;=> [1]
;= 4
;;; replace (+ foo x) with (+ foo bar x) in the above
;;; to get a CompilerException

Dynamic let form as part of reify within a macro

Ok, let's try to get this straight: my final intent is to provide a macro as an API to users which will look like:
(defscript [a b]
(println a))
The result has to be an instance of a Script protocol, which looks like:
(defprotocol Script
(run [this model]))
The idea being that the first argument to defscript is a list of symbols that needs to be bound to correspondent keys in the model:
(.run (defscript [a b] (println a)) {:a 1}) ;; yields 1
I can't come up with any code that can effectively produce such effect, as I'm constantly hitting a wall when trying to use the model parameter, since at macro expansion time it's just a symbol:
(defmacro invoke-
[params model body]
(let [p (flatten (map (fn [x] [x (model (keyword x))]) params))]
`(let [~#p]
~body)))
(defmacro defscript
[params & body]
`(reify Script
(run [~'this ~'model]
(invoke- ~params ~'model ~#body))))
invoke- works fine if called directly:
(invoke- [a] {:a 1} (println a)) ;; prints 1
but it doesn't work when used within defscript as model can't be expanded correctly:
(.run (defscript [a] (println a)) {:a 1}) ;; prints nil
How can I get past this point and glue the pieces together?
It seems that basically, your argument vector is a shortcut for a destructuring binding:
(defscript [a b] body) -> (reify Script (run [this {:keys [a b]}] body))
That way, model is destructured at run time, as it should be.

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}>