taylor_venable wrote:I think I understand that when a macro is "called" (encountered at compile time) it is evaluated, and any result is expanded into its position in the surrounding code verbatim (so to speak), then at run time it (that is, the result of the expansion) is evaluated.
"encountered" is a good word. The best way to think about a macro is that it's a part of the compiler
that you have written. It's a function that extends the compiler with your special syntax. When the compiler encounters code with your macro in it, it passes all that list structure that looks like parameters, unevaluated, to the macro. The macro hands back whatever list structure it wants, and the compiler then replaces the macro site with that replacement structure and keeps compiling. This can happen multiple times, recursively, until the compiler expands the whole Lisp form to something with all macros removed. At that point, the compiler can then compile everything similar to what it would do in a C compiler and such.
So, a macro is nothing more than a function that gets called at compile time to transform the input code to something that you want the compiler to then digest.
So my question, stated with the most precision I can muster at this stage of my learning, is, given that a macro seems to at most be able to refer to the text (so to speak) of its arguments, does that not necessarily mean that once one is using a macro (let's call it m) which does not itself evaluate the arguments internally, that from there on "up" in the code the arguments to that macro m cannot be received in (and therefore expanded into) evaluated form without explicitly using eval in a macro that expands "around" m?
I have to admit that I read the above several times and I don't quite understand what you're getting at. An example would be great here to help clarify what you're saying.
That said, let me see if I can give you a bit more information and see if that helps jiggle something.
A macro receives, through its parameters, the exact list structure that is located at those parameter spots. Being a function that is executed at compile-time, the macro can literally
do anything with that list structure that it wants. The macro is a function running in the context of this large Lisp program called the compiler. So, the macro could, if it wanted, evaluate that list structure using eval, it could print it on the printer, it could open up a socket and post it to LispForum (please don't write a macro to do that
), etc. Most of the time, a macro just manipulates the list structure to rearrange it and combine it with other list structure in some way to generate more efficient code or replace what would otherwise be a lot of repetitive typing, etc.
Now, if I understand what you mean by "see," then yes a single macro can't "see" any of the code in the list structure that surrounds the macro, in the same way that a function can't "see" any parameters passed to another function which calls it. Everybody can only "see" their local parameters. That said, you can write macros that work together or use conventions to pass data between themselves. For instance, you could have a global variable named *foo* that one or more macros set or read. Generally, this is bad and it's a sign that you're either up against a really tough problem or you're going about things the wrong way, but sometimes there is a need for it.
So, per my situation currently, with-html-output-to-string
is a macro that receives its argument as text, and as such it will never do what I am wanting unless I explicitly generate the result by calling eval
and inject the result using a comma within a back-quote, for example, as I have shown above -- in other words, without using eval
it will always either operate on "form" or on "(car '((:p "foo")))", neither one of which is what I want. Is this correct? I'm not particularly worried about the Lisp eval
so if this is the way I end up doing it, no problem. I just want to make sure I understand why
I have to do it this way; if I'm not right on by now, hopefully I'm getting at least a little closer.
I would check the library to see if it has a way to do what you want, which is include computed values. If it doesn't, then perhaps rather than twisting the library using EVAL, you should choose a new library, or hack the source code if you have it to do what you want.
In this case, CL-WHO is written by Edi Weitz. Edi is a great
Lisp hacker, as you'll soon learn, and his libraries smoke! They're very
high quality, have great documentation, and Edi usually thinks of just about everything.
I have never used CL-WHO myself, but I perused the docs online and came to this section:
Here, you can read that Edi has provided for just the situation that you are interested in. Specifically, he gives you the STR, FMT, ESC, and HTM escapes. These are tags in the arguments to WITH-HTML-OUTPUT-TO-STRING that are processed specially by the macro. Essentially, the macro walks through the list structure you give it as an argument and interprets it (it's literally a small interpreter running in the context of the macro). Typically, it interprets it by creating code that generates HTML output, but when it encounters one of these tags, it takes some list structure that you the user have included and injects it into the macro expansion. Then, when the macro hands all that back to the compiler, the compiler will arrange to have your forms called at just the right time in the middle of all that output.
But it seems that this is the way things work, so now I'm wondering if there are any short-cuts to doing this, like a special form in CL that basically wraps a macro with a call to eval like this for you, or if it is my duty to write this code myself. It just seems odd to me that in the last week I've twice run into the situation where I wanted to evaluate the argument to a macro, and have it expand into code that contains the evaluated form, rather than the textual form. But perhaps it's because I'm new at using macros and don't yet know when and how it's appropriate to use them. In any case, thanks again for your help.
Generally, whenever you reach for EVAL, it's the wrong tool. As a beginner, it's tempting, because it's right there. But once the light-bulb goes off, you'll realize that you almost never need it. About the only legitimate use of EVAL is when you're getting code from the outside world during the runtime of the program and you want to evaluate it (like a REPL, for instance). Other than that, if you have the code that you think you want to EVAL at compile time, EVAL is most probably the wrong tool.
Thanks for the reference, I'll definitely check this out tonight and over the weekend to try to get a better feel of how to do what I want to accomplish. I'm getting the feeling that there's a substantial gap in either my understanding of the system or how I'm thinking about the problem, so reading up on it and writing some more code should surely help.
No problem. Once you have had a chance to digest all this and you think you're starting to "get it," I'd read On Lisp by Paul Graham for a heavy duty dosage of macrology.