If your macros is 'fixed' to emulate function call semantics by evaluating its arguments only once, then maybe a function is a more appropriate abstraction in the first place. The whole point of macros is that they let you break the rules of function call application in hopefully useful and predictable ways.
Another way to look at it is that repeatedly evaluating an argument is what you do NOT want for a macro like 'getc', but probably what you DO want for a macro like 'repeat'. The danger lies in the fact that it's hard to tell the difference when looking at a call site in isolation.
Whether or not that danger is an acceptable risk is, of course, situation dependent.
All the programmer has to do is achieve run-time coverage to catch all the problems.
We could define a getc-like macro such that getc(*stream++) would diagnose, provided that the line is executed.
See sfx.h and sfx.c here: http://git.savannah.nongnu.org/cgit/kazlib.git/tree/
;; cond evaluated exactly once;
;; then or else at most once, not before cond.
(if cond then else)
getc doesn't have to be a macro. It illustrates just the point that the macro issues in C are so unfixable that broken macros have even been codified in ISO C. (if* cond
#'(lambda () then)
#'(lambda () else))
All the macro does is eliminate the need to write out all the lambda syntax. (defmacro (if cond then else)
`(if* ,cond
#'(lambda () ,then)
#'(lambda () ,else)))
This brings my back to my original point: "the extent of the logic encoded in macros should be minimized, with the macros translating the code pretty much straight away into something built on more functional abstractions.".Works in C too, although not as nicely:
void dscwritef_impl(const _TCHAR * format_str, ...);
#define pdscwritef(flag, args) \
do { if (DEBUG_FLAG(flag)) dscwritef_impl args; } while(0);
The funny thing is, I think we're largely in agreement.Huh? Are you saying the use of lambdas does NOT give if* the ability to control the execution of 'then' and 'else'?
My point is that if you're concerned about how often you evaluate a block of code (0, 1, or n times), there are ways to achieve this goal that do not require macros. (And consequently, the macros mainly serve as they should: to clean up the syntax, if necessary.)
I gave if as an example; it was not intended to be an example of a macro which has to go out of its way to ensure once-only evaluation.
There are common examples of macros that use machine-generated unique variables to hold the results of evaluating an argument form, in order to be able to insert that value into multiple places in the generated code. An implementation of with-slots likely has to, for instance.
In documenting a library of C macros, we cannot specify a strict evaluation order without seriously constraining which of those macros are actually implementable.
I think that came from me. My point was mainly that 1) control over evaluation is a significant reason to use macros 2) there are other ways to achieve that goal and 3) those other ways should be used to the extent possible. To me at least, this diminishes the value of one of the key headline features of the Lisp family of languages.
Note that this does not mean that I don't want to use the language.'I've maintained a personal and professional interest in the language that dates back over 25 years. When I have the choice, I usually reach for Lisp (really Clojure these days) as the most effective way to write the software I have the time and interest to write. It's just that the reasons for this don't center around the idea of compile time code transformation. (As useful as that can be when needed.)
> There are common examples of macros that use machine-generated unique variables to hold the results of evaluating an argument form, in order to be able to insert that value into multiple places in the generated code. An implementation of with-slots likely has to, for instance.
I do know this, because I've written at least a few of them myself.
https://github.com/mschaef/vcsh/blob/255afa60adf180e7e3e5b6e...
https://github.com/mschaef/vcsh/blob/255afa60adf180e7e3e5b6e...
Edit:
> ... insert that value into multiple places in the generated code.
Is that really what you meant to say? You're using a machine generated variable in a macro to 'insert a _value_ into multiple places in the generated code'? (As in, the value itself gets emitted in the generated code?)
The code I link to above does something slightly different. What it does is generate code that uses a machine generated variable to hold the result of a single execution of an expression. It then inserts references to that machine generated unique variable in multiple places in the generated code.
C macros have the issue that even when everyone involved in the creation and use of a C macro understands its pifalls, those pitfalls cannot be removed from the macro.
For instance, a certain C macro might evaluate some expression twice. Everyone knows that this is dangerous, but there isn't any way to fix it. They just document it.
ISO C itself says that getc may evaluate its argument multiple times; thus don't do things like getc(stream_array[i++]) unless you remove the macro definition wth #undef.
Lisp macros do not have issues that are unfixable in this way.
Sometimes they have issues that are difficult, though not impossible. Usually that occurs when, to be perfect, the macro would have to do a full-blown code walk. Macros are written that do code walks (for instance the iterate macro).
> Put succinctly, the problem I write about in the blog post is that macros are essentially always inlined into the output of the compiler. This has the effect of more tightly coupling the modules together than is evident in the surface syntax. (Note that macro invocation sites are syntactically indistinguishable from function invocation sites, which makes this problem worse.)
Before you apply macros, you need a well-designed (and documented, and versioned!) API against which the macros will write the code. If all you care about is what the macro syntax looks like and don't put any design into how the expansion works (beyond just massaging it so that it somehow works), then you may run into problems.
Macros don't introduce any problems that writing the same code by hand against the same API's wouldn't introduce.
If someone has to write the code, I don't see how you can get around it: it's either going to be a human, or a macro.