6 ms·
But 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 pa
by reitzensteinm 8y ago
But 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.
- lispm 8y ago> But an infix transform does delay the evaluation of expressions you've passed to it. Eg (infix (avg b) * (sgn a)). It doesn't 'delay' anything. It just rewrites at compile time (infix (avg b) * (sgn a)) into (* (avg b) (sgn a)) That's all. At runtime only the rewritten statement will be executed. > At a very low level, macros operate by having the evaluation of their arguments delayed Not at all. The main purpose of macros is to compute code from arbitrary expressions at compile time. There is no 'delay' -> it's mostly about rewriting expressions at compile time. > implementing a metacircular evaluator and learning when and how to perform evaluation. You can build a macro expansion phase into an interpreter. That's what Lisp interpreters do. But the main purpose of macros is to enable code transformations at compile time and thus only to have a cost at compile time, not at runtime. In an interpreter the macro expansions would also happen at runtime and have a cost there. But Lisp compilers are popular and to make the most out of code transformations at compile time, the idea of macro and their expansion at compile-time was introduced. Thus one gets for example new embedded languages, without the need to parse them at runtime.
- reitzensteinm 8y agoI believe that if you're trying to understand what macros do from a fundamental level, treating them like functions with different semantics is a very good learning method. You implement them in a SICP style interpreter and then write a few. Totally clears up the mystery. I'm not arguing with you about what happens in any production lisp. I'm sure you know more than me, but I do understand how they're implemented in practice, and the warts that brings - eg no (apply or <list>), and first class macros are so esoteric purely from the runtime cost that they're barely even written about in the literature. I'm just saying the mental model of where macros fit in in an interpreter has value, and that view has not changed. In my opinion you're an expert coming in correcting a bunch of people attempting to learn the basics, and you're not wrong, but you are muddying the waters.
- kd0amg 8y agotreating them like functions with different semantics is a very good learning method If you mean run-time functions that act a bit funny about evaluation order, that mental model is incompatible with common introductory examples like `let`.
- reitzensteinm 8y agoNo, it's not. You eval the return value of the call. There is no loss of expressive power. Compile time macro expansion in this model is simply partial evaluation.
- kd0amg 8y agoIf `let` is a function, how and when are its arguments (say, `((x 2) (y 3))` and `(+ x y)`) evaluated?
- reitzensteinm 8y agoThe call would return: ((fn [x] ((fn [y] (+ x y)) 3)) 2) Which is in turn evaluated as you'd expect.