5 ms·
That might be more like what was called an FEXPR mechanism in some early Lisps or some niche Lisps, or even in languages like R. https://en.wikipedia.org/wiki/
by lispm 8y ago
That might be more like what was called an FEXPR mechanism in some early Lisps or some niche Lisps, or even in languages like R.
https://en.wikipedia.org/wiki/Fexpr https://en.wikipedia.org/wiki/Fexpr
When an FEXPR is called, it gets unevaluated args and can then at runtime decide what to do.
Macros OTOH are a different mechanism, where expressions get rewritten at macro expansion time, which can be for example at compile time. The macro then gets called with arbitrary expressions it encloses (those don't need to be valid code by themselves) and computes new source.
Thus macros are code generators from expressions. In a compiled implementation, macro expansion is done with compilation interleaved: each form gets expanded, even repeatedly until it no longer expands into a macro, and then the resulting non-macro form gets compiled.
Thus in a way a macro does not delay execution, it does the opposite: it actually shifts computation to compile time -> the computation of code from expressions and the computation of arbitrary side effects in the compile-time environment.
In an interpreter version of Lisp, the macro gets also expanded at runtime - but in its own macroexpansion during evaluation. There eval will call the macroexpander repeatedly until it gets a non-macro form.
Now, what can you do with arbitrary complex code generators at compile time? https://stackoverflow.com/a/2563308/69545 https://stackoverflow.com/a/2563308/69545
Actually Paul Graham wrote a classical Lisp book explaining a bunch of things around macros. Available here for download:
http://www.paulgraham.com/onlisp.html http://www.paulgraham.com/onlisp.html
The classic Lisp article which motivated the move from FEXPRs to macros:
https://www.nhplace.com/kent/Papers/Special-Forms.html https://www.nhplace.com/kent/Papers/Special-Forms.html
- reitzensteinm 8y agoI don't think what I wrote is a FEXPR, which doesn't evaluate the result of the function call. I'm having a hard time parsing what you wrote or any of the sources you linked in such a way that says the semantics of macros differs from what I wrote (certainly the implementation gets a lot more complex and there are subtleties).
- lispm 8y agoThat's why I wrote 'more like'. Given that you haven't defined any semantics of your operators, it's more like a guess. Not sure if this helps you. But let's define a macro A: CL-USER 32 > (defmacro a (b c) (print (list :macro-expansion b c)) (list 'print (list 'quote (list :runtime :b b :c c)))) A This macro does two things: it prints something at macro expansion time and then generates some code it returns as a value. Now we can use this macro in some code: CL-USER 33 > (defun test () (a 21 42)) TEST If we now compile the function, the macro gets executed and prints something: CL-USER 34 > (compile 'test) (:MACRO-EXPANSION 21 42) TEST NIL NIL We can also call the macroexpander independent of the compiler. The we a) get the side effect of the print statement and we see the result: CL-USER 35 > (macroexpand '(a 20 30)) (:MACRO-EXPANSION 20 30) (PRINT (QUOTE (:RUNTIME :B 20 :C 30))) T At runtime we call the test function: CL-USER 36 > (test) (:RUNTIME :B 21 :C 42) ; <- the printed output (:RUNTIME :B 21 :C 42) ; print also returns its arg Thus all the macro expansions have been done at compile time and we have generated some code there. No macro expansion at runtime. Thus it has to do with code generation and code execution at macro expansion - nothing about 'delaying' something. The example isn't useful, but imagine a macro INFIX (infix a * b + c) which rewrites the expression to the Lisp expression: (+ (* a b) c) There is nothing about 'delaying' -> it's just rewriting the form. Ideally at compile time. There are many other examples which do something different.
- reitzensteinm 8y agoBut an infix transform does delay the evaluation of expressions you've passed to it. Eg (infix (avg b) * (sgn a)). And most practical uses of macros involve passing in expressions that will be later evaluated verbatim, e.g. (time (reduce + (range 100))) I was only intending responding to this: "That's a common misconception that Lisp macros are mostly used to 'delay' evaluation." Which just seems to me confuses the matter in response to the article that was posted. At a very low level, macros operate by having the evaluation of their arguments delayed. At a higher level, they're used to implement sugared forms which usually contain valid code (though not necessarily code that makes sense in isolation). I know you're very experienced with lisp, and I'm not trying to correct you technically, but to me this stuff only clicked when implementing a metacircular evaluator and learning when and how to perform evaluation. The blog post to me reads like the author was going through a similar process.
- thesz 8y agohttps://www.quotes.net/quote/36312 https://www.quotes.net/quote/36312 "The primary purpose of the DATA statement is to give names to constants; instead of referring to pi as 3.141592653589793 at every appearance, the variable PI can be given that value with a DATA statement and used instead of the longer form of the constant. This also simplifies modifying the program, should the value of PI change." Or, quoting the other aphorism, what is constant for someone is a variable for someone else. What is run time for LISP (which lacked good compiler at early stages - the famous two horse asses width constraint) can freely be a compile time for other language. Or for a LISP, for that matter, in our time. The compile time expansion allows for better (because faster) error checking.