I'm trying to write a macro that calls some functions. The functions should only be used by the macro, so I put them inside of a letfn
wrapping the macro. Pseudocode:
(letfn [(fn-a [] ...)
(fn-b [] ...)
(fn-c [] (fn-b))]
(defmacro my-macro [stuff]
`(let [whatever# (fn-a)]
(fn-c))))
The calls to fn-a
and fn-c
work, but when fn-c
tries to call fn-b
I get IllegalStateException: Attempting to call unbound fn: #'name.space/fn-b. Why is that?
If I put fn-b
and fn-c
in their own defn
s, everything works. But I don't want to do that because it isn't clean.
Edit: Just to test, I tried putting the function bindings in the inner let
but ran into the same exception.
I don't think this can work at all this way.
The call to fn-c
for example gets expanded to your.namespace/fn-c
, so your code seems to call other functions that just happen to have the same names.
But you dont have a your.namespace/fn-b
, which raises the exception.
To refer to a unqualified symbol you need to quote and unquote it: ~'fn-a
But this won't work either because the local functions aren't defined at the point of expansion, you can only use them for the macro itself.
You either have to define the functions in a namespace and qualify them in the macro or include them in the macroexpansion, which would define them again at each use.
I'm not sure if this is exactly what you're after but if I do:
(letfn [(fn-a [] (println 1))
(fn-b [] (println 2))
(fn-c [] (fn-b))]
(defmacro my-macro [stuff]
`(let [whatever# ~(fn-b)]
~(fn-c))))
then it works - it just needs the tilde to unquote the function calls.
Both of the following work:
(defn fn-x [] (println 1))
(defn fn-y [] (fn-x))
(defmacro my-macro2 [stuff]
`(let [whatever# (fn-x)]
(fn-y)))
AND
(defn fn-x [] (println 1))
(defn fn-y [] (fn-x))
(defmacro my-macro2 [stuff]
`(let [whatever# ~(fn-x)]
~(fn-y)))
In the first case, the functions are being evaluated and their results incorporated into the macro at compile time whereas in the second they are being evaluated at runtime. With the letfn
(which is a macro itself) the results are not available at compile time (presumably because they are being compiled after your macro is compiled) so the functions can only be used at runtime.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With