Hello.
I’m having a problem with macro expansion.
This is my macro:
lfe> (defmacro foo (body)
`(progn
,@body))
lfe> (foo (io:format "hello~n"))
** exception error: symbol io:format is unbound
Now when looking at the expansion it gets clear why it doesn’t work:
lfe> (macroexpand '(foo (io:format "hello~n")) $ENV)
(progn io:format "hello~n")
But I don’t know what I did wrong. The macro seems OK to me.
Can anyone spot the issue?
Manfred
3 Likes
No LFE or lisp expert here, but I was interested : )
If you wrap your args twice in parens, it’ll work. This makes sense because :
This does not work.
lfe> (progn io:format "hello~n")
** exception error: symbol io:format is unbound
But this works :
(progn (io:format "hello~n"))
hello
ok
Thus,
lfe> (defmacro foo (body) `(progn ,@body))
foo
lfe> (foo ((io:format "hello~n")))
hello
ok
Without the double parens and by the time @body is referred to in the macro it is the equivalent of
lfe> io:format "hello~n"
** exception error: symbol io:format is unbound
It’s not clear to me how to wrap @body
in the macro with parens so you could avoid doing it in the call to foo
though.
You could also simply do :
lfe> (defmacro foo (body) body))
foo
lfe> (foo (io:format "hello~n"))
hello
ok
But there may be problems with that at compile time?
I would ask @rvirding or @oubiwann for a technical explanation on this one
2 Likes
Thanks for responding.
I know from Common Lisp, that the &rest, or &body arguments in macros automatically add extra parens around the argument value, which are then again removed by the splice. Because the body could consist of multiple forms/sexprs.
However, this doesn’t seem to be the case here. It should IMO not ne necessary to manually wrap it in more parens.
2 Likes
There are a couple of things which cause this problem:
progn
takes a list of forms to evaluate and as your expansion expanded to
(progn io:format “hello~n”)`
then it started evaluating each form one after the other. So first io:format
which is an unbound symbol.
- It’s in the definition of the macro and and in
,@body
. When you define the macro as (defmacro foo (body) ...)
you are saying that the macro when called should get one and only on argument and body
will refer to that argument. So when you call (foo (io:format "hello~n"))
then body
will get the value (io:format "hello~n")
. Now in a backquote using ,@
to expand a value will splice that list into the expansion which is why you got the flattened progn
. This is how CL defines the backquote and how it expands.
So if you define the macro as
(defmacro foo body `(progn ,@body))
then body
will refer to the list of all arguments and the expansion will become as you wished it to be.
lfe> (defmacro foo body `(progn ,@body))
foo
lfe> (foo (io:format “hello~n”))
hello
ok
lfe> (macroexpand '(foo (io:format “hello~n”)) $ENV)
(progn (io:format “hello~n”))
lfe> (foo (io:format “hello~n”) (io:format “there~n”))
hello
there
ok
lfe> (macroexpand '(foo (io:format “hello~n”) (io:format “there~n”)) $ENV)
(progn (io:format “hello~n”) (io:format “there~n”))
If you look in the doc/lfe_guide.txt in the Macros section it describes this and gives an example which is close to your macro.
2 Likes
OK, thanks for your response.
I was actually looking through the documentation. Now that you say it I looked again and found this one:
(defmacro my-list args
"List of arguments."
`(list ,@args))
Which indeed looks like what I need. But I guess the argument without parenthesis got me distracted and I thought: that cannot be what I need.
So effectively a single argument without parenthesis represents a list of forms as body similar as the &rest
, or &body
arguments in CL.
Excellent I will remember this.
3 Likes